home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 5817 / 5817.xpi / chrome / content / sqlitemanager.js < prev    next >
Text File  |  2010-02-11  |  97KB  |  2,797 lines

  1. Components.utils.import("resource://sqlitemanager/sqlite.js");
  2. Components.utils.import("resource://sqlitemanager/tokenize.js");
  3.  
  4. // SQLiteManager extension
  5. var Database = new SQLiteHandler();
  6. Database.setFuncConfirm(SmGlobals.confirmBeforeExecuting);
  7.  
  8. var smStructTrees = [];
  9. smStructTrees[0] = new TreeDbStructure("t-dbStructNorm", "tc-dbStructNorm", 0);
  10.  
  11. var treeBrowse = new TreeDataTable("browse-tree");
  12. var treeExecute = new TreeDataTable("treeSqlOutput");
  13.  
  14. var smExtManager = null;
  15.  
  16. var SQLiteManager = {
  17.   msQuerySelectInstruction: null,
  18.   prefs: null,
  19.  
  20.   miDbObjects: 0,
  21.   //for display in the browse tree
  22.   miLimit: -1,
  23.   miOffset: 0,
  24.   miCount: 0,
  25.  
  26.   maSortInfo: [],
  27.   msBrowseObjName: null,
  28.   msBrowseCondition: null,
  29.  
  30.   // Currently selected database file (nsIFile)
  31.   sCurrentDatabase: null, 
  32.  
  33.   //an array containing names of current table, index, view and trigger
  34.   aCurrObjNames: [],
  35.  
  36.   //to store the latest selection in tree showing db objects
  37.   mostCurrObjName: null,
  38.   mostCurrObjType: null,
  39.  
  40.   mbDbJustOpened: true,
  41.   miDbInfoCallCount: 0,
  42.   // an array of 4 arrays;
  43.   // each holding names of tables, indexes, views and triggers
  44.   aObjNames: [],
  45.   aObjTypes: ["master", "table", "view", "index", "trigger"],
  46.  
  47.   clipService: null,   // Clipboard service: nsIClipboardHelper
  48.  
  49.   // Status bar: panels for displaying various info
  50.   sbPanel: [],
  51.  
  52.   maFileExt: [],
  53.  
  54.   //Purpose: generates foreign key triggers based on
  55.   //http://www.sqlite.org/cvstrac/wiki?p=ForeignKeyTriggers
  56.   //TODO: remove this function after sqlite 3.6.19 and use PRAGMA foreign_keys instead
  57.   generateFKTriggers: function() {
  58.     var sTableName = this.aCurrObjNames["table"];
  59.     var allRows = Database.getForeignKeyList(sTableName, "");
  60.     if (allRows.length == 0) {
  61.       alert(sm_getLStr("sqlm.noForeignKey"));
  62.       return false;
  63.     }
  64.  
  65.     var iId = 0;
  66.     var aTemp = [], aFkeys = [];
  67.     for (var i = 0; i < allRows.length; i++) {
  68.       fk = allRows[i];
  69.       if (!Database.tableExists(fk.table, "")) {
  70.         alert(sm_getLFStr("sqlm.fKeyNoTable",[fk.table]));
  71.         return false;
  72.       }
  73.       if (fk.table == sTableName) {
  74.         alert(sm_getLStr("sqlm.fKeySelfReference"));
  75.         return false;
  76.       }
  77.       if (fk.to == null) {
  78.         alert(sm_getLFStr("sqlm.fKeyUnnamedColumn",[fk.from]));
  79.         return false;
  80.       }
  81.       if (fk.id == iId) {
  82.         aTemp.push(fk);
  83.       }
  84.       else {
  85.         aFkeys.push(aTemp);
  86.         aTemp = [fk];
  87.       }
  88.       
  89.     }
  90.     if (i > 0)
  91.       aFkeys.push(aTemp);
  92.  
  93.     var aQ = [];
  94.     for (var i=0; i < aFkeys.length; i++) {
  95.       var aOneKey = aFkeys[i];
  96.  
  97.       var sOnUpdate = aOneKey[0].on_update;
  98.       var sOnDelete = aOneKey[0].on_delete;
  99.       var sToTable = aOneKey[0].table;
  100.       var id = aOneKey[0].id;
  101.  
  102.       var sSelectColsTo = "", sSelectColsFrom = "", sWhereTo = "", sWhereFrom = "", sNullCols = "", sSetClause = "";
  103.  
  104.       for (var j=0; j < aOneKey.length; j++) {
  105.         var oneRow = aOneKey[j];
  106.         if (j != 0) {
  107.           sSelectColsTo += ", ";
  108.           sSelectColsFrom += ", ";
  109.           sSetClause += ", ";
  110.           sWhereTo += " AND ";
  111.           sWhereFrom += " AND ";
  112.         }
  113.         sSelectColsTo += '"' + oneRow.to + '"';
  114.         sSelectColsFrom += '"' + oneRow.from + '"';
  115.         sWhereTo += '"' + oneRow.to + '" = NEW."' + oneRow.from + '"';
  116.         sWhereFrom += '"' + oneRow.from + '" = OLD."' + oneRow.to + '"';
  117.         sSetClause += '"' + oneRow.from + '" = NEW."' + oneRow.to + '"';
  118.         //if from column is not notnull
  119.         var aFromCols = Database.getTableInfo(sTableName, "");
  120.         for (var k = 0; k < aFromCols.length; k++) {
  121. //        alert(k + " : " + oneRow.from + " : " + aFromCols[k].name + " : " + aFromCols[k].notnull);
  122.           if (oneRow.from == aFromCols[k].name && aFromCols[k].notnull == 0) {
  123.             sNullCols += ' NEW."' + oneRow.from + '" IS NOT NULL AND '; 
  124.           }
  125.         }
  126.       }
  127.       //now try creating strings for trigger name, etc.
  128.       var sFkeyTrigPrefix = "_fk";
  129.  
  130.       //1. insert on child table
  131.       var insTrigName = sFkeyTrigPrefix + "_" + sTableName + "_insert_" + id;
  132.       var sError = "'insert on table " + sTableName + " violates foreign key constraint'";
  133.       aQ.push('DROP TRIGGER IF EXISTS ' + insTrigName);
  134.       aQ.push('CREATE TRIGGER IF NOT EXISTS ' + insTrigName + ' BEFORE INSERT ON "' + sTableName + '" FOR EACH ROW BEGIN SELECT RAISE(ROLLBACK, ' + sError + ") WHERE " + sNullCols + " (SELECT " + sSelectColsTo + ' FROM "' + sToTable + '" WHERE ' + sWhereTo + ') IS NULL; END;');
  135.  
  136.       //2. update on child table
  137.       var updTrigName = sFkeyTrigPrefix + "_" + sTableName + "_update_" + id;
  138.       var sError = "'update on table " + sTableName + " violates foreign key constraint'";
  139.       aQ.push('DROP TRIGGER IF EXISTS ' + updTrigName);
  140.       aQ.push('CREATE TRIGGER IF NOT EXISTS ' + updTrigName + ' BEFORE UPDATE ON "' + sTableName + '" FOR EACH ROW BEGIN SELECT RAISE(ROLLBACK, ' + sError + ") WHERE " + sNullCols + " (SELECT " + sSelectColsTo + ' FROM "' + sToTable + '" WHERE ' + sWhereTo + ') IS NULL; END;');
  141.  
  142.       //3. delete on parent table
  143.       var delTrigName = sFkeyTrigPrefix + "_" + sTableName + "_delete_" + id;
  144.       var sError = "'delete on table " + sToTable + " violates foreign key constraint'";
  145.       aQ.push('DROP TRIGGER IF EXISTS ' + delTrigName);
  146.       if (sOnDelete.toUpperCase() == "CASCADE") {
  147.         aQ.push('CREATE TRIGGER IF NOT EXISTS ' + delTrigName + ' BEFORE DELETE ON "' + sToTable + '" FOR EACH ROW BEGIN DELETE FROM "' + sTableName + '" WHERE ' + sWhereFrom + '; END;');
  148.       }
  149.       else {
  150.         aQ.push('CREATE TRIGGER IF NOT EXISTS ' + delTrigName + ' BEFORE DELETE ON "' + sToTable + '" FOR EACH ROW BEGIN SELECT RAISE(ROLLBACK, ' + sError + ') WHERE (SELECT ' + sSelectColsFrom + ' FROM "' + sTableName + '" WHERE ' + sWhereFrom + ') IS NOT NULL; END;');
  151.       }
  152.  
  153.       //4. update on parent table
  154.       var updParTrigName = sFkeyTrigPrefix + "_" + sTableName + "_updateParent_" + id;
  155.       var sError = "'update on table " + sToTable + " violates foreign key constraint'";
  156.       aQ.push('DROP TRIGGER IF EXISTS ' + updParTrigName);
  157.       if (sOnUpdate.toUpperCase() == "CASCADE") {//after update
  158.         aQ.push('CREATE TRIGGER IF NOT EXISTS ' + updParTrigName + ' AFTER UPDATE ON "' + sToTable + '" FOR EACH ROW BEGIN UPDATE "' + sTableName + '" SET ' + sSetClause + " WHERE " + sWhereFrom + '; END;');
  159.       }
  160.       else {
  161.         aQ.push('CREATE TRIGGER IF NOT EXISTS ' + updParTrigName + ' BEFORE UPDATE ON "' + sToTable + '" FOR EACH ROW BEGIN SELECT RAISE(ROLLBACK, ' + sError + ') WHERE (SELECT ' + sSelectColsFrom + ' FROM "' + sTableName + '" WHERE ' + sWhereFrom + ') IS NOT NULL; END;');
  162.       }
  163.     }
  164.  
  165.     if (sm_confirm(sm_getLStr("sqlm.confirm.title"), sm_getLStr("sqlm.confirm.msg") + aQ.join('\n\n'))) {
  166.       Database.executeSimpleSQLs(aQ);    
  167.       this.refreshDbStructure();
  168.     }
  169.   },
  170.  
  171.   experiment: function() {
  172.     document.querySelector('treechildren::-moz-tree-cell(nullvalue selected)');
  173.   },
  174.  
  175.   copyText: function (sText) {
  176.     this.clipService.copyString(sText);
  177.   },
  178.  
  179.   // Startup: called ONCE during the browser window "load" event
  180.   Startup: function() {
  181.     $$("experiment").hidden = true;
  182.  
  183.     if (SmGlobals.gecko_193a1) {
  184.       $$("btn-foreign-key").hidden = true;
  185.     }
  186.  
  187.     this.msQuerySelectInstruction = sm_getLStr("sqlm.selectQuery");
  188.  
  189.     SmUdf.init();
  190.  
  191.     //create the menus by associating appropriate popups
  192.     this.createMenu();
  193.  
  194.     //initialize the structure tree
  195.     smStructTrees[0].init();
  196.  
  197.     this.refreshDbStructure();
  198.  
  199.     treeBrowse.init();
  200.     treeExecute.init();
  201.  
  202.     var mi = $$("menu-general-sharedPagerCache");
  203.  
  204.     // Load clipboard service
  205.     this.clipService = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
  206.  
  207.     //get the nodes for Status bar panels
  208.     this.sbPanel["display"] = $$("sbPanel-display");
  209.     //global var in globals.js for sbpanel[display]
  210.     SmGlobals.sbPanelDisplay = this.sbPanel["display"];
  211.  
  212.     //display Gecko version & extVersion in the status bar
  213.     $$("sbExtVersion").label = SmGlobals.extVersion;
  214.     $$("sbGeckoVersion").label = "Gecko " + SmGlobals.appInfo.platformVersion;
  215.  
  216.     smHide(["vb-structureTab", "vb-browseTab", "vb-executeTab", "vb-dbInfoTab"]);
  217.  
  218.     //preferences service to add oberver
  219.     // and then observe changes via the observe function
  220.     //see http://developer.mozilla.org/en/docs/Adding_preferences_to_an_extension
  221.     //initialize the preference service with the correct branch
  222.     this.prefs = sm_prefsBranch;
  223.     //query interface to be able to use addObserver method
  224.     this.prefs.QueryInterface(Ci.nsIPrefBranch2);
  225.     //now, add the observer which will be implemented using observe method
  226.     //calling removeObserver when done with observing helps the memory
  227.     this.prefs.addObserver("", this, false);
  228.  
  229.     var iNumRecords = sm_prefsBranch.getIntPref("displayNumRecords");
  230.     if (iNumRecords == -1)
  231.       sm_prefsBranch.setIntPref("displayNumRecords", 100);
  232.  
  233.     //To set our variables, etc. we fool observe into believing that the following preferences have changed.
  234.     for(var i = 0; i < SmGlobals.observedPrefs.length; i++)
  235.       this.observe("", "nsPref:changed", SmGlobals.observedPrefs[i]);
  236.  
  237.     //1. xulrunner application.ini -f xyz:
  238.     //    if -f argument is present, then open the corresponding param (xyz) or none if it fails
  239.     //    else open last db
  240.     //2. extension: open last db
  241.  
  242.     var bOpenLastDb = true;
  243.     //proceed to check commandline arguments only if we are in an xulrunner app
  244.     if(SmGlobals.appInfo.name == 'sqlite-manager') {
  245.       if (window.arguments) {
  246.         //commandline arguments if running with xulrunner
  247.         try {
  248.           var cmdLine = window.arguments[0];
  249.           cmdLine = cmdLine.QueryInterface(Ci.nsICommandLine);
  250.           var fArg = cmdLine.handleFlagWithParam("f", true);
  251.           if (fArg != null) {
  252.             bOpenLastDb = false;
  253.             var file = cmdLine.resolveFile(fArg);
  254.             this.sCurrentDatabase = file;
  255.             this.setDatabase(this.sCurrentDatabase);
  256.             if (this.sCurrentDatabase == null)
  257.               alert('Failed to connect to ' + file.path);
  258.           }
  259.         } catch (e) {
  260.           sm_log('Command line error: ' + e.message);
  261.         }
  262.       }
  263.     }
  264. ///////////////////////////////////////////////////////////////
  265.     if(SmGlobals.appInfo.name == 'Firefox') {
  266.       if (window.arguments && window.arguments.length) {
  267.         if (typeof window.arguments[0] == "string") {
  268.           var sPath = window.arguments[0];
  269.           bOpenLastDb = false;
  270.           var file = FileIO.getFile(sPath);
  271.           //if the file is not found, bail out
  272.           if(!file.exists()) {
  273.             smPrompt.alert(null, sm_getLStr("extName"), sm_getLFStr("dbDoesNotExist",[sPath]));
  274.           }
  275.           else {
  276.             this.sCurrentDatabase = file;
  277.             this.setDatabase(this.sCurrentDatabase);
  278.             if (this.sCurrentDatabase == null)
  279.               alert('Failed to connect to ' + file.path);
  280.           }
  281.         }
  282.       }
  283.     }
  284.  
  285. ///////////////////////////////////////////////////////////////
  286.  
  287.     //try opening the last db
  288.     if (bOpenLastDb)
  289.       this.openLastDb();
  290.  
  291.     //load the previously opened tab
  292.     this.loadTabWithId(this.getSelectedTabId());
  293.     return;
  294.   },
  295.  
  296.   // Shutdown: called ONCE during the browser window "unload" event
  297.   Shutdown: function() {
  298.     //close the current database
  299.     this.closeDatabase(false);
  300.     //Destruction - this should be done once you're done observing
  301.     //Failure to do so may result in memory leaks.  
  302.     this.prefs.removeObserver("", this);
  303.  
  304.     this.clipService= null;
  305.     SmUdf.close();
  306.   },  
  307.  
  308.   openLastDb: function() {
  309.     // opening with last used DB if preferences set to do so
  310.     var bPrefVal = sm_prefsBranch.getBoolPref("openWithLastDb");
  311.     if(!bPrefVal)
  312.       return;
  313.  
  314.     var sPath = SmGlobals.mru.getLatest();
  315.     if(sPath == null) 
  316.       return;
  317.  
  318.     //Last used DB found, open this DB
  319.     var newfile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
  320.     try {
  321.       newfile.initWithPath(sPath);
  322.     } catch (e) {
  323.       smPrompt.alert(null, sm_getLStr("extName"), 'Failed to init local file using ' + sPath);
  324.       return;
  325.     }
  326.     //if the last used file is not found, bail out
  327.     if(!newfile.exists()) {
  328.       smPrompt.alert(null, sm_getLStr("extName"), sm_getLFStr("lastDbDoesNotExist",[sPath]));
  329.       return;
  330.     }
  331.  
  332.     bPrefVal = sm_prefsBranch.getBoolPref("promptForLastDb");
  333.     if(bPrefVal) {
  334.       var check = {value: false}; // default the checkbox to false
  335.       var result = smPrompt.confirmCheck(null, sm_getLStr("extName") + " - " + sm_getLStr("promptLastDbTitle"), sm_getLStr("promptLastDbAsk")+ "\n" + sPath + "?", sm_getLStr("promptLastDbOpen"), check);
  336.  
  337.       if(!result)
  338.         return;
  339.       //update the promptForLastDb preference
  340.       bPrefVal = sm_prefsBranch.setBoolPref("promptForLastDb", !check.value);
  341.     }
  342.     //assign the new file (nsIFile) to the current database
  343.     this.sCurrentDatabase = newfile;
  344.     this.setDatabase(this.sCurrentDatabase);
  345.   },
  346.  
  347.   createMenu: function() {
  348.     var suffixes = ["table", "index", "view", "trigger"];
  349.  
  350.     var mpdb = $$("mp-dbstructure");
  351.     for(var i = 0; i < suffixes.length; i++) {
  352.       var suffix = suffixes[i];
  353.       var mp = $$("menu-" + suffix);
  354.       var ch = mp.querySelector('menupopup').childNodes;
  355.       for (var c = 0; c < ch.length; c++) {
  356.         var clone = ch[c].cloneNode(true);
  357.         clone.setAttribute("smType", suffix);
  358.         mpdb.appendChild(clone);
  359.       }
  360.       var mp = $$("mp-create-" + suffix);
  361.       var ch = mp.childNodes;
  362.       for (var c = 0; c < ch.length; c++) {
  363.         var clone = ch[c].cloneNode(true);
  364.         clone.setAttribute("smType", "create-" + suffix);
  365.         mpdb.appendChild(clone);
  366.       }
  367.     }
  368.   },
  369.  
  370.   changeDbSetting: function(sSetting) {
  371.     if (sSetting == "schema_version") {
  372.       var bConfirm = sm_confirm(sm_getLStr("dangerous.op"), sm_getLStr("confirm.changeSchemaVersion") + "\n\n" + sm_getLStr("q.proceed"));
  373.       if (!bConfirm)
  374.         return false;
  375.     }
  376.     var node = $$("pr-" + sSetting);
  377.     var sVal = node.value;
  378.     var newVal = Database.setSetting(sSetting, sVal);
  379.     node.value = newVal;
  380.  
  381.     var sMessage = sm_getLFStr("pragma.changed", [sSetting, newVal]);
  382.     sm_notify("boxNotifyDbInfo", sMessage, "info");
  383.   },
  384.  
  385.   setTreeStructureContextMenu: function() {
  386.     var tree = $$(smStructTrees[this.miDbObjects].treeId);
  387.     var idx = tree.currentIndex;
  388.     // idx = -1 if nothing is selected; says xulplanet element reference
  389.     if(idx == -1)
  390.       idx = 0;
  391.     var objName = tree.view.getCellText(idx, tree.columns.getColumnAt(0));
  392.     var level = tree.view.getLevel(idx);
  393.     var info = smStructTrees[this.miDbObjects].getSmType(idx);
  394.  
  395.     //there is a database object at level 1 only
  396.     var mpId = "";
  397.     if (level == 0) {
  398.       if(info.indexOf("all-") == 0) {
  399.         info = info.substring("all-".length).toLowerCase();
  400.         if (this.aObjTypes.indexOf(info) > 0) //thus omit master
  401.           mpId = "create-" + info;
  402.       }
  403.     }  
  404.     if (level == 1) {
  405.        if (this.aObjTypes.indexOf(info) != -1)
  406.          mpId = info;
  407.     }
  408.     var mpdb = $$("mp-dbstructure");
  409.     var ch = mpdb.childNodes;
  410.     for(var i = 0; i < ch.length; i++) {
  411.       var suffix = ch[i].getAttribute("smType");
  412.       if (suffix == mpId)
  413.         ch[i].hidden = false;
  414.       else
  415.         ch[i].hidden = true;
  416.     }
  417.   },
  418.  
  419.   showMruList: function() {
  420.     var aList = SmGlobals.mru.getList();
  421.  
  422.     var menupopupNode = $$("menu-mru").firstChild;
  423.     SmGlobals.$empty(menupopupNode);
  424.     for (var i = 0; i < aList.length; i++) {
  425.       var mp = $$("mi-mru");
  426.       var mi = mp.cloneNode(true);
  427.       mi.setAttribute("id", "mi-mru-" + i);
  428.       mi.setAttribute("label", aList[i]);
  429.       mi.removeAttribute("hidden");
  430.       menupopupNode.appendChild(mi);
  431.     }
  432.   },
  433.  
  434.   observe: function(subject, topic, data) {
  435.     if (topic != "nsPref:changed")
  436.       return;
  437.     
  438.     switch(data) {
  439.       case "jsonDataTreeStyle":
  440.         if (SmGlobals.stylerDataTree.addTreeStyle())
  441.           this.loadTabBrowse();
  442.         break;
  443.       case "jsonMruData":
  444.         this.showMruList();
  445.         break;
  446.       case "hideMainToolbar":
  447.         var bPrefVal = sm_prefsBranch.getBoolPref("hideMainToolbar");
  448.         $$("hbox-main-toolbar").hidden = bPrefVal;
  449.         break;
  450.       case "showMainToolbarDatabase":
  451.         var bPrefVal = sm_prefsBranch.getBoolPref("showMainToolbarDatabase");
  452.         $$("sm-toolbar-database").hidden = !bPrefVal;
  453.         break;
  454.       case "showMainToolbarTable":
  455.         var bPrefVal = sm_prefsBranch.getBoolPref("showMainToolbarTable");
  456.         $$("sm-toolbar-table").hidden = !bPrefVal;
  457.         break;
  458.       case "showMainToolbarIndex":
  459.         var bPrefVal = sm_prefsBranch.getBoolPref("showMainToolbarIndex");
  460.         $$("sm-toolbar-index").hidden = !bPrefVal;
  461.         break;
  462.       case "showMainToolbarDebug":
  463.         var bPrefVal = sm_prefsBranch.getBoolPref("showMainToolbarDebug");
  464.         $$("sm-toolbar-debug").hidden = !bPrefVal;
  465.         break;
  466.       case "sqliteFileExtensions":
  467.         var sExt = sm_prefsBranch.getCharPref("sqliteFileExtensions");
  468.         this.maFileExt = sExt.split(",");
  469.         for (var iC = 0; iC < this.maFileExt.length; iC++) {
  470.           this.maFileExt[iC] = this.maFileExt[iC].trim();
  471.         }
  472.         // Load profile folder's sqlite db files list into dropdown 
  473.         this.populateDBList("profile");   
  474.         break;
  475.       case "searchToggler":
  476.         //Issue #285: get unicode string
  477.         var sPrefVal = sm_prefsBranch.getComplexValue("searchCriteria", Ci.nsISupportsString).data;
  478.         if (sPrefVal != "nocriteria") {
  479.           this.msBrowseCondition = sPrefVal;
  480.           //because search criteria has changed, set offset for navigating to zero
  481.           this.miOffset = 0;
  482.         }
  483.         //empty the criteria after use for security
  484.         sm_prefsBranch.setCharPref("searchCriteria", "");
  485.         this.loadTabBrowse();
  486.         break;
  487.       case "displayNumRecords":
  488.         var iPrefVal = sm_prefsBranch.getIntPref("displayNumRecords");
  489.         this.miLimit = iPrefVal;
  490.         if (this.miLimit == 0) this.miLimit = -1;
  491.         this.miOffset = 0;
  492.         break;
  493.       case "identifierQuoteChar":
  494.         var sQuoteChar = sm_prefsBranch.getCharPref("identifierQuoteChar");
  495.         SQLiteFn.setQuoteChar(sQuoteChar);
  496.         break;
  497.       case "textForBlob":
  498.       case "showBlobSize":
  499.       case "maxSizeToShowBlobData":
  500.       case "blob.howToShowData":
  501.         var obj = {};
  502.         obj.sStrForBlob = sm_prefsBranch.getCharPref("textForBlob");
  503.         obj.bShowSize = sm_prefsBranch.getBoolPref("showBlobSize");
  504.         obj.iMaxSizeToShowData = sm_prefsBranch.getIntPref("maxSizeToShowBlobData");
  505.         obj.iHowToShowData = sm_prefsBranch.getIntPref("blob.howToShowData");
  506.  
  507.         Database.setBlobPrefs(obj);
  508.         break;
  509.       case "handleADS": //for ADS on Windows/NTFS
  510.         $$("mi-connect-ads-win").hidden = true;
  511.         if (navigator.oscpu.indexOf("Windows") >= 0) {
  512.           var iPrefVal = sm_prefsBranch.getIntPref("handleADS");
  513.           if (iPrefVal == 1)
  514.             $$("mi-connect-ads-win").hidden = false;
  515.         }
  516.         break;
  517.       case "posInTargetApp":
  518.       if(SmGlobals.appInfo.ID == "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}") {
  519.         var md = window.QueryInterface(Ci.nsIInterfaceRequestor)
  520.           .getInterface(Ci.nsIWebNavigation)
  521.           .QueryInterface(Ci.nsIDocShellTreeItem).rootTreeItem
  522.           .QueryInterface(Ci.nsIInterfaceRequestor)
  523.           .getInterface(Ci.nsIDOMWindow).document;
  524.         var iVal = sm_prefsBranch.getIntPref("posInTargetApp");
  525.         var mi = md.getElementById("menuitem-sqlitemanager");
  526.         if (mi) {
  527.           if (iVal == 0)
  528.             mi.setAttribute("hidden", true);
  529.           if (iVal == 1)
  530.             mi.setAttribute("hidden", false);
  531.         }
  532.       }
  533.     }
  534.   },
  535.  
  536.   refresh: function() {
  537.     if (this.sCurrentDatabase == null)
  538.       return false;
  539.     this.refreshDbStructure();
  540.     return true; 
  541.   },
  542.   //Issue #108
  543.   reconnect: function() {
  544.     //check whether the file still exists
  545.     if(!this.sCurrentDatabase.exists()) {
  546.       alert(sm_getLStr("sqlm.alert.fileNotFound") + this.sCurrentDatabase.path);
  547.       this.closeDatabase(false);
  548.       SmGlobals.mru.remove(sPath);
  549.       this.sCurrentDatabase = null;
  550.       this.setDatabase(this.sCurrentDatabase);
  551.       return true;
  552.     }
  553.  
  554.     var sPath = this.sCurrentDatabase.path;
  555.     //Issue #149: must connect in exclusive mode to connect to the actual file rather than the cached file; correspondingly, make exclusive mode the default one.
  556.     $$("menu-general-sharedPagerCache").removeAttribute("checked");
  557.     this.openDatabaseWithPath(sPath);
  558.   },
  559.  
  560.   //refreshDbStructure: populates the schematree based on selected database
  561.   //must be called whenever a database is opened/closed
  562.   //and whenever the schema changes
  563.   refreshDbStructure: function() {
  564.     //1. if no database is selected
  565.     if (this.sCurrentDatabase == null) {
  566.       smStructTrees[0].removeChildData();
  567.  
  568.       for(var i = 0; i < this.aObjTypes.length; i++) {
  569.         var type = this.aObjTypes[i];
  570.         this.aCurrObjNames[type] = null;
  571.       }
  572.       return;
  573.     }
  574.  
  575.     //2. if db is being opened, set nodes to expand
  576.     if (this.mbDbJustOpened) {
  577.       //set the expandable nodes here
  578.       var aExpand = [["all-table"],[]];
  579.       //check whether aExpand data is in smextmgmt table and use it
  580.       if (smExtManager.getUsage()) {
  581.         aExpand = smExtManager.getStructTreeState();
  582.       }
  583.       smStructTrees[0].setExpandableNodes(aExpand);
  584.     }
  585.  
  586.     //3. 
  587.     var tree = $$(smStructTrees[this.miDbObjects].treeId);
  588.  
  589.     //requery for all the objects afresh and redraw the tree
  590.     for (var iC = 0; iC < this.aObjTypes.length; iC++) {
  591.       var sType = this.aObjTypes[iC];
  592.       this.aObjNames[sType] = Database.getObjectList(sType, "");
  593.     }
  594.  
  595.     var idx = tree.currentIndex;
  596.     smStructTrees[this.miDbObjects].setChildData(this.aObjNames);
  597.  
  598.     if (idx >= smStructTrees[this.miDbObjects].visibleDataLength)
  599.       idx = 0;
  600.  
  601.     tree.view.selection.select(idx); //triggers getDbObjectInfo function
  602.     
  603.     //now assign the current objects
  604.     for(var i = 0; i < this.aObjTypes.length; i++) {
  605.       var type = this.aObjTypes[i];
  606.       if(this.aObjNames[type].length > 0) {
  607.         var bFound = false;
  608.         if(this.aCurrObjNames[type]) {
  609.           for(var n = 0; n < this.aObjNames[type].length; n++) {
  610.             if(this.aCurrObjNames[type] == this.aObjNames[type][n]) {
  611.               bFound = true;
  612.               break;
  613.             }
  614.           }
  615.         }
  616.         if(!bFound)
  617.           this.aCurrObjNames[type] = this.aObjNames[type][0];
  618.       }
  619.       else
  620.         this.aCurrObjNames[type] = null;
  621.     }
  622.   },
  623.           
  624.   //getDbObjectInfo: this function must show the structural info about the
  625.   // selected database object (table, index, view & trigger)
  626.   //this function is triggered by the select event on the tree
  627.   getDbObjectInfo: function() {
  628.     this.miDbInfoCallCount++;
  629.  
  630.     var tree = $$(smStructTrees[this.miDbObjects].treeId);
  631.     var idx = tree.currentIndex;
  632.  
  633.     // idx = -1 if nothing is selected; says xulplanet element reference
  634.     if(idx < 0 || idx >= tree.view.rowCount)
  635.       idx = 1; //first table
  636.  
  637.     var level = tree.view.getLevel(idx);
  638.  
  639.     var r_name, r_type;
  640.     //there is a database object at level 1 only
  641.     if(level == 0) {
  642.       if (this.miDbInfoCallCount > 1) {
  643.         this.mostCurrObjName = null;
  644.         this.mostCurrObjType = null;
  645.         return false;
  646.       }
  647.       else {
  648.         r_name = 'sqlite_master';
  649.         r_type = 'master';
  650.       }
  651.     }
  652.     else {
  653.       //level 2 is a field name of the parent table
  654.       if(level == 2) {
  655.         idx = tree.view.getParentIndex(idx);
  656.       }  
  657.       r_name = tree.view.getCellText(idx, tree.columns.getColumnAt(0));
  658.       r_type = smStructTrees[this.miDbObjects].getSmType(idx);
  659.     }  
  660.       
  661.     //assign current selection in tree as current object
  662.     this.aCurrObjNames[r_type] = r_name;
  663.  
  664.     this.mostCurrObjName = r_name;
  665.     this.mostCurrObjType = r_type;
  666.  
  667.     this.loadTabStructure();
  668.     this.loadTabBrowse();
  669.  
  670.     return true;
  671.   },
  672.  
  673.   hideTabStructure: function() {
  674.     //hide the hboxes containing object specific operation buttons; later enable one appropriate hbox according to the selection in tree
  675.     smHide(["d-master-ops", "d-more-info", "gb-master-info"]);
  676.   },
  677.  
  678.   emptyTabStructure: function() {
  679.     //hide the hboxes containing object specific operation buttons
  680.     //later enable one appropriate hbox according to the selection in tree
  681.     this.hideTabStructure();
  682.  
  683.     $$("str-sql").value = "";
  684.  
  685.     this.printTableInfo(null, "table");
  686.   },
  687.  
  688.   loadTabStructure: function() {
  689.     //no need to waste resources if this tab is not selected
  690.     if(this.getSelectedTabId() != "tab-structure")
  691.       return false;
  692.  
  693.     this.hideTabStructure();
  694.     this.cancelEditColumn();
  695.  
  696.     if (this.sCurrentDatabase == null)
  697.       return false;
  698.  
  699.     //there is a database object at level 1 only
  700.     if(this.mostCurrObjName == null) {
  701.       return false;
  702.     }  
  703.     
  704.     var r_name = this.mostCurrObjName;
  705.     var r_type = this.mostCurrObjType;
  706.  
  707.     $$("d-master-ops").hidden = false;
  708.     $$("d-master-ops").selectedPanel = $$("gb-master-ops-" + r_type);
  709.  
  710.     if (r_name == "sqlite_master" || r_name == "sqlite_temp_master") {
  711.       $$("cap-object-info").label = 'TABLE' + ': ' + r_name;
  712.     }
  713.     else {
  714.       var row = Database.getMasterInfo(r_name, '');
  715.       $$("cap-object-info").label = row.type.toUpperCase() + ': ' + row.name;
  716.       if (row.sql != null) {
  717.         $$("gb-master-info").hidden = false;
  718.         $$("str-sql").value = row.sql;
  719.         $$("desc-sql").textContent = row.sql;
  720.  
  721.         //let there be no scrollbars in the textbox
  722.         var iMinRows = 1, iMaxRows = 20;
  723.         if (row.type == 'table' || row.type == 'index') {
  724.           iMaxRows = 10;
  725.         }
  726.         var ta = $$("str-sql");
  727.         adjustTextboxRows(ta, iMinRows, iMaxRows);
  728.       }
  729.     }
  730.     //do the following for table/index
  731.     if(r_type == "table" || r_type == "master") {
  732.       this.printTableInfo(this.aCurrObjNames[r_type], r_type);
  733.     }
  734.     if(r_type == "index") {
  735.       this.printIndexInfo(this.aCurrObjNames[r_type]);
  736.     }
  737.     return true;
  738.   },
  739.  
  740.   printTableInfo: function(sTable, sType) {
  741.     if (this.miDbObjects == 1) //no add column option for master tables
  742.       sType = "master";
  743.  
  744.     $$("d-more-info").hidden = false;
  745.     $$("d-more-info").selectedPanel = $$("gb-more-info-table");
  746.  
  747.     SmGlobals.$empty($$("smTableColumns"));
  748.     if (sTable == null)
  749.       return;
  750.  
  751. ////////////////////////////////////
  752.     if (Database.getOpenStatus() != "Exclusive") {
  753. //      $$("treeTabCols").datasources = "file://" + Database.getFile().path;
  754. //      $$("qPragmaTable").textContent = "PRAGMA table_info('" + sTable + "')";
  755. //      $$("treeTabCols").builder.rebuild();
  756.     }
  757.  
  758. ////////////////////////////////////
  759.     smShow(["hb-addcol", "mp-opTableColumn"]);
  760.     if (sTable.indexOf("sqlite_") == 0) {
  761.       //no add/edit/drop column for master tables
  762.       smHide(["hb-addcol", "mp-opTableColumn"]);
  763.      }
  764.  
  765.     $$("treeTabCols").setAttribute("smTableName", sTable);
  766.     var cols = Database.getTableInfo(sTable, "");
  767.     $$("capColumns").label = $$("capColumns").getAttribute("labelPrefix") + " (" + cols.length + ")";
  768.  
  769.     SmGlobals.$empty($$("smTableColumns"));
  770.     var hhh = '';
  771.     for(var i = 0; i < cols.length; i++) {
  772. /* the following is useful with jquery
  773.       hhh += '<treeitem><treerow>';
  774.       hhh += '<treecell label="' + cols[i].cid + '"/>';
  775.       hhh += '<treecell label="' + cols[i].name + '"/>';
  776.       hhh += '<treecell label="' + cols[i].type + '"/>';
  777.       hhh += '<treecell label="' + cols[i].notnull + '"/>';
  778.       hhh += '<treecell label="' + cols[i].dflt_value + '"/>';
  779.       hhh += '<treecell label="' + cols[i].pk + '"/>';
  780.       hhh += '</treerow/></treeitem>';
  781. */
  782.       var trow = document.createElement("treerow");
  783.  
  784.       var tcell = document.createElement("treecell");
  785.       tcell.setAttribute("label", cols[i].cid);
  786.       trow.appendChild(tcell);
  787.  
  788.       var tcell = document.createElement("treecell");
  789.       tcell.setAttribute("label", cols[i].name);
  790.       trow.appendChild(tcell);
  791.  
  792.       var tcell = document.createElement("treecell");
  793.       tcell.setAttribute("label", cols[i].type);
  794.       trow.appendChild(tcell);
  795.  
  796.       var tcell = document.createElement("treecell");
  797.       tcell.setAttribute("label", cols[i].notnull);
  798.       trow.appendChild(tcell);
  799.  
  800.       var tcell = document.createElement("treecell");
  801.       tcell.setAttribute("label", cols[i].dflt_value);
  802.       if (cols[i].dflt_value == null)
  803.         tcell.setAttribute("class", "nullvalue");
  804.       trow.appendChild(tcell);
  805.  
  806.       var tcell = document.createElement("treecell");
  807.       tcell.setAttribute("label", cols[i].pk);
  808.       trow.appendChild(tcell);
  809.  
  810.       var titem = document.createElement("treeitem");
  811.       titem.appendChild(trow);
  812.       $$("smTableColumns").appendChild(titem);
  813.     }
  814.     //TODO: sort this funny issue!! the assignment below succeeds but when I select a master table, the border of the tree encloses only so many rows as were visible in the previously shown table.
  815.     //TODO: set a user-pref for max/min rows or a splitter?
  816.     var iRows = (cols.length <= 5)? 5 : cols.length;
  817.     $$("treeTabCols").setAttribute("rows", cols.length);
  818.  
  819.     var aObj = Database.getObjectCount(sTable, "");
  820.     $$("numRecords").value = Database.getRowCount(sTable, "");
  821.     $$("numIndexes").value = aObj.indexCount;
  822.     $$("numTriggers").value = aObj.triggerCount;
  823.   },
  824.  
  825.   printIndexInfo: function(sIndex) {
  826.     $$("d-more-info").hidden = false;
  827.     $$("d-more-info").selectedPanel = $$("gb-more-info-index");
  828.  
  829.     var aIndexInfo = Database.getIndexDetails(sIndex, '');
  830.     $$("tabletoindex").value = aIndexInfo.tbl_name;
  831.     $$("duplicatevalues").value = sm_getLStr("allowed");
  832.     if(aIndexInfo.unique == 1)
  833.       $$("duplicatevalues").value = sm_getLStr("notAllowed");
  834.  
  835.     var cols = Database.getIndexInfo(sIndex, "");
  836.  
  837.     SmGlobals.$empty($$("smIndexColumns"));
  838.     for(var i = 0; i < cols.length; i++) {
  839.       var trow = document.createElement("treerow");
  840.  
  841.       var tcell = document.createElement("treecell");
  842.       tcell.setAttribute("label", cols[i].seqno);
  843.       trow.appendChild(tcell);
  844.  
  845.       var tcell = document.createElement("treecell");
  846.       tcell.setAttribute("label", cols[i].cid);
  847.       trow.appendChild(tcell);
  848.  
  849.       var tcell = document.createElement("treecell");
  850.       tcell.setAttribute("label", cols[i].name);
  851.       trow.appendChild(tcell);
  852.  
  853.       var titem = document.createElement("treeitem");
  854.       titem.appendChild(trow);
  855.       $$("smIndexColumns").appendChild(titem);
  856.     }
  857.   },
  858.  
  859.   changeSortOrder: function(ev) {
  860.     if (ev.button==2) //right click
  861.       return;
  862.     var tgt = ev.target
  863.     var sColName = tgt.getAttribute("label");
  864.     var bFound = false;
  865.     for(var i = 0; i < this.maSortInfo.length; i++) {
  866.       if (this.maSortInfo[i][0] == sColName) {
  867.         bFound = true;
  868.         switch (this.maSortInfo[i][1]) {
  869.           case "none":
  870.             this.maSortInfo[i][1] = "asc";
  871.             break;
  872.           case "asc":
  873.             this.maSortInfo[i][1] = "desc";
  874.             break;
  875.           case "desc":
  876.             this.maSortInfo[i][1] = "none";
  877.             break;
  878.         }
  879.         var aTemp = this.maSortInfo[i];
  880.         this.maSortInfo.splice(i, 1);
  881.  
  882.         if (aTemp[1] != "none")
  883.           this.maSortInfo.splice(0, 0, aTemp);
  884.       }
  885.     }
  886.     if (!bFound)
  887.       this.maSortInfo.splice(0, 0, [sColName, "asc"]);
  888.     this.loadTabBrowse();
  889.   },
  890.  
  891.   //loadTabBrowse: populates the table list and the tree view for current table; must be called whenever a database is opened/closed and whenever the schema changes; depends entirely upon the values in "browse-type" and "browse-name" controls
  892.   loadTabBrowse: function() {
  893.     //no need to waste resources if this tab is not selected
  894.     if(this.getSelectedTabId() != "tab-browse")
  895.       return false;
  896.       
  897.     if (this.sCurrentDatabase == null)
  898.       return false;
  899.  
  900.     if (this.mostCurrObjType == null)
  901.       return false;
  902.  
  903.     var sObjType = this.mostCurrObjType.toLowerCase();
  904.     if (sObjType != "table" && sObjType != "master" && sObjType != "view")
  905.       return false;
  906.  
  907.     $$("browse-type").value = sObjType.toUpperCase();
  908.     if ($$("browse-name").value != this.mostCurrObjName)
  909.       this.maSortInfo = [];
  910.  
  911.     $$("browse-name").value = this.mostCurrObjName;
  912.  
  913.     //populate the treeview
  914.     var sObjName = this.mostCurrObjName;
  915.     if (sObjName != this.msBrowseObjName) {
  916.       this.miOffset = 0;
  917.       this.msBrowseObjName = sObjName;
  918.       this.msBrowseCondition = "";
  919.     }
  920.  
  921.     //some UI depends on whether table/master tables/view is shown
  922.     var btnAdd =  $$("btnAddRecord");
  923.     var btnDup =  $$("btnAddDupRecord");
  924.     var btnEdit =  $$("btnEditRecord");
  925.     var btnDelete =  $$("btnDeleteRecord");
  926.  
  927.     var treeChildren = $$("browse-treechildren");
  928.  
  929.     var setting = [false, "mp-editTableRow", "SQLiteManager.operateOnTable('update')"];
  930.     if (sObjType == "table" && (this.mostCurrObjName == "sqlite_master" || this.mostCurrObjName == "sqlite_temp_master")) {
  931.       setting = [true, "mp-browse-copy", ""];
  932.     }
  933.     if (sObjType == "master") {
  934.       setting = [true, "mp-browse-copy", ""];
  935.     }
  936.     if (sObjType == "view") {
  937.       setting = [true, "mp-browse-copy", ""];
  938.     }
  939.  
  940.     btnAdd.disabled = setting[0];
  941.     btnDup.disabled = setting[0];
  942.     btnEdit.disabled = setting[0];
  943.     btnDelete.disabled = setting[0];
  944.     treeChildren.setAttribute("context", setting[1]);
  945.     treeChildren.setAttribute("ondblclick", setting[2]);
  946.  
  947.     treeBrowse.ShowTable(false);
  948.  
  949.     try {
  950.       var aArgs = {sWhere: this.msBrowseCondition, iLimit: this.miLimit, iOffset: this.miOffset, aOrder: this.maSortInfo};
  951.       var iRetVal = Database.loadTableData(sObjType, sObjName, aArgs);
  952.       var timeElapsed = Database.getElapsedTime();
  953.     } catch (e) { 
  954.       sm_message(e + "\n" + sm_getLStr("loadDataFailed"), 0x3);
  955.       return false;
  956.     }
  957.     if (iRetVal == -1)
  958.       return false;
  959.  
  960.     var records = Database.getRecords();
  961.     var types = Database.getRecordTypes();
  962.     var columns = Database.getColumns();
  963.     this.miCount = Database.getRowCount(sObjName, this.msBrowseCondition);
  964.     $$("sbQueryTime").label = "ET: " + timeElapsed;
  965.  
  966.     this.manageNavigationControls();      
  967.     if (records && columns) {
  968.       $$("browse-tree").setAttribute("smObjType", sObjType);
  969.       $$("browse-tree").setAttribute("smObjName", sObjName);
  970.       treeBrowse.createColumns(columns, iRetVal, this.maSortInfo, "SQLiteManager.changeSortOrder(event);");
  971.       var jsonColInfo = smExtManager.getBrowseTreeColState(sObjType, sObjName);
  972.       if (jsonColInfo != "") {
  973.         var objColInfo = JSON.parse(jsonColInfo);
  974.         treeBrowse.adjustColumns(objColInfo);
  975.       }
  976.       treeBrowse.PopulateTableData(records, columns, types);
  977.     }
  978.     return true;
  979.   },
  980.   //Issue #378
  981.   copyColumnName: function(ctrl) {
  982.     alert(ctrl.tagName);
  983.     alert(ctrl.parentNode.tagName);
  984.     alert(ctrl.parentNode.parentNode.tagName);
  985.   },
  986.  
  987.   onBrowseNavigate: function(sType) {
  988.     switch(sType) {
  989.       case "first":
  990.         this.miOffset = 0;
  991.         break;
  992.       case "previous":
  993.         this.miOffset = this.miOffset - this.miLimit;
  994.         if (this.miOffset < 0)
  995.           this.miOffset = 0;
  996.         break;
  997.       case "next":
  998.         this.miOffset = this.miOffset + this.miLimit;
  999.         break;
  1000.       case "last":
  1001.         this.miOffset = this.miCount - (this.miCount % this.miLimit);
  1002.         break;
  1003.     }
  1004.     this.loadTabBrowse();
  1005.   },
  1006.  
  1007.   manageNavigationControls: function() {
  1008.     //manage textboxes
  1009.     $$("nav-total-val").value = this.miCount;
  1010.     var iStart = (this.miCount == 0) ? 0 : (this.miOffset + 1);
  1011.     $$("nav-start-val").value = iStart;
  1012.     var iEnd = this.miOffset + this.miLimit;
  1013.     iEnd = ((iEnd > this.miCount) || (this.miLimit <= 0)) ? this.miCount : iEnd;
  1014.     $$("nav-end-val").value = iEnd;
  1015.  
  1016.     //manage buttons
  1017.     var btnFirst = $$("btn-nav-first");
  1018.     var btnPrevious = $$("btn-nav-previous");
  1019.     var btnNext = $$("btn-nav-next");
  1020.     var btnLast = $$("btn-nav-last");
  1021.  
  1022.     btnFirst.disabled = false;
  1023.     btnPrevious.disabled = false;
  1024.     btnNext.disabled = false;
  1025.     btnLast.disabled = false;
  1026.  
  1027.     //manage the navigate buttons
  1028.     if (this.miLimit < 0 || this.miLimit >= this.miCount) {
  1029.       btnFirst.disabled = true;
  1030.       btnPrevious.disabled = true;
  1031.       btnNext.disabled = true;
  1032.       btnLast.disabled = true;
  1033.       return;
  1034.     }
  1035.  
  1036.     if (this.miOffset == 0) {
  1037.       btnFirst.disabled = true;
  1038.       btnPrevious.disabled = true;
  1039.     }
  1040.     else {
  1041.       btnFirst.disabled = false;
  1042.       btnPrevious.disabled = false;
  1043.     }
  1044.     if (this.miOffset + this.miLimit > this.miCount) {
  1045.       btnNext.disabled = true;
  1046.       btnLast.disabled = true;
  1047.     }
  1048.     else {
  1049.       btnNext.disabled = false;
  1050.       btnLast.disabled = false;
  1051.     }
  1052.   },
  1053.  
  1054.   //loadTabExecute: anything to be done when that tab is shown goes here
  1055.   loadTabExecute: function() {
  1056.     this.populateQueryListbox();
  1057.   },
  1058.  
  1059.   //loadTabDbInfo: anything to be done when that tab is shown goes here
  1060.   loadTabDbInfo: function() {
  1061.     //no need to waste resources if this tab is not selected
  1062.     if(this.getSelectedTabId() != "tab-dbinfo")
  1063.       return false;
  1064.  
  1065.     if (this.sCurrentDatabase == null)
  1066.       return false;
  1067.  
  1068.     var aSettings = ["schema_version", "user_version", "auto_vacuum", "cache_size", /*"case_sensitive_like",*/ "count_changes", "default_cache_size", "empty_result_callbacks", "encoding", "full_column_names", "fullfsync", "journal_mode", "journal_size_limit", "legacy_file_format", "locking_mode", "page_size", "max_page_count", "page_count", "freelist_count", "read_uncommitted", "reverse_unordered_selects", "short_column_names", "synchronous", "temp_store", "temp_store_directory"];
  1069.  
  1070.     if (SmGlobals.gecko_193a1) {
  1071.       aSettings.push("foreign_keys");
  1072.       aSettings.push("recursive_triggers");
  1073.  
  1074.       $$("hb-pr-foreign_keys").hidden = false;
  1075.       $$("hb-pr-recursive_triggers").hidden = false;
  1076.     }
  1077.  
  1078.     for(var i = 0; i < aSettings.length; i++)  {
  1079.       var sSetting = aSettings[i];
  1080.       var node = $$("pr-" + sSetting);
  1081.       var newVal = Database.getSetting(sSetting);
  1082.       node.value = newVal;
  1083.     }
  1084.     return true;
  1085.   },
  1086.  
  1087.   search: function() {
  1088.     var oType = $$("browse-type").value.toUpperCase();
  1089.     var oName = $$("browse-name").value;
  1090.     if (oType == "VIEW")
  1091.       return this.searchView(oName);
  1092.     if (oType == "TABLE" || oType == "MASTER") {
  1093.       window.openDialog("chrome://sqlitemanager/content/RowOperations.xul", "RowOperations", "chrome, resizable, centerscreen, modal, dialog", Database, oName, "search");
  1094.       return true;
  1095.     }
  1096.   },
  1097.  
  1098.   searchView1: function(sViewName) {
  1099.     var aArgs = {sWhere: "", iLimit: 1, iOffset: 0};
  1100.     Database.loadTableData("view", sViewName, aArgs);
  1101.     var records = Database.getRecords();
  1102.     if (records.length == 0) {
  1103.       alert(sm_getLStr("noRecord"));
  1104.       return false;
  1105.     }
  1106.  
  1107.     var columns = Database.getColumns();
  1108.     var names = [], types = [];
  1109.     for (var col in columns) {
  1110.       names[col] = columns[col][0];
  1111.       types[col] = '';
  1112.     }
  1113.     var aColumns = [names, types];
  1114.     
  1115.     this.aFieldNames = aColumns[0];
  1116.     var aTypes = aColumns[1];
  1117.  
  1118.     var grbox = $$("hb-sliding");
  1119.     SmGlobals.$empty(grbox);
  1120. //        var cap = document.createElement("caption");
  1121. //        cap.setAttribute("label", "Enter Field Values");
  1122. //        grbox.appendChild(cap);
  1123.  
  1124.     for(var i = 0; i < this.aFieldNames.length; i++) {
  1125.       var hbox = document.createElement("hbox");
  1126.       hbox.setAttribute("flex", "1");
  1127.       hbox.setAttribute("style", "margin:2px 3px 2px 3px");
  1128.  
  1129.       var lbl = document.createElement("label");
  1130.       var lblVal = (i+1) + ". " + this.aFieldNames[i];
  1131.       lblVal += " ( " + aTypes[i] + " )"; 
  1132.       lbl.setAttribute("value", lblVal);
  1133.       lbl.setAttribute("style", "padding-top:5px;width:25ex");
  1134.       lbl.setAttribute("accesskey", (i+1));
  1135.       lbl.setAttribute("control", "ctrl-" + this.aFieldNames[i]);
  1136.       hbox.appendChild(lbl);
  1137.  
  1138.       var spacer = document.createElement("spacer");
  1139.       spacer.flex = "1";
  1140.       hbox.appendChild(spacer);
  1141.  
  1142.       var vb = RowOperations.getSearchMenuList(this.aFieldNames[i]);
  1143.       hbox.appendChild(vb);
  1144.  
  1145.       var inp = RowOperations.getInputField(i);
  1146.       hbox.appendChild(inp);
  1147.  
  1148.       var vb = RowOperations.getInputToggleImage(i);
  1149.       hbox.appendChild(vb);
  1150.  
  1151.       grbox.appendChild(hbox);
  1152.     }
  1153.     return true;
  1154.   },
  1155.  
  1156.   searchView: function(sViewName) {
  1157.     var aArgs = {sWhere: "", iLimit: 1, iOffset: 0};
  1158.     Database.loadTableData("view", sViewName, aArgs);
  1159.     var records = Database.getRecords();
  1160.     if (records.length == 0) {
  1161.       alert(sm_getLStr("noRecord"));
  1162.       return false;
  1163.     }
  1164.  
  1165.     var columns = Database.getColumns();
  1166.     var names = [], types = [];
  1167.     for (var col in columns) {
  1168.       names[col] = columns[col][0];
  1169.       types[col] = '';
  1170.     }
  1171.     var cols = [names, types];
  1172.     window.openDialog("chrome://sqlitemanager/content/RowOperations.xul",  "RowOperations", "chrome, resizable, centerscreen, modal, dialog", Database, sViewName, "search-view", cols);
  1173.     return true;
  1174.   },
  1175.  
  1176.   showAll: function() {
  1177.     sm_prefsBranch.setCharPref("searchCriteria", "");
  1178.  
  1179.     //the value of searchToggler should toggle for change event to fire.
  1180.     var bTemp = sm_prefsBranch.getBoolPref("searchToggler");
  1181.     sm_prefsBranch.setBoolPref("searchToggler", !bTemp);
  1182.   },
  1183.  
  1184.   //getSelectedTabId: returns the id of the selected tab
  1185.   getSelectedTabId: function() {
  1186.     return $$("sm-tabs").selectedItem.id;
  1187.   },
  1188.  
  1189.   //selectStructTab: called when onselect event fires on tabs[id="sm-tabs-db"]
  1190.   selectStructTab: function(oSelectedTab) {
  1191.     var id = oSelectedTab.getAttribute("id");
  1192.     switch(id) {
  1193.       case "tab-db-norm":
  1194.         this.miDbObjects = 0;
  1195.         break;
  1196.     }
  1197.     this.refreshDbStructure();
  1198.     return true;
  1199.   },
  1200.  
  1201.   loadTabWithId: function(sId) {
  1202.     switch(sId) {
  1203.       case "tab-structure":
  1204.         this.loadTabStructure();
  1205.         break;
  1206.       case "tab-browse":
  1207.         this.loadTabBrowse();
  1208.         break;
  1209.       case "tab-execute":
  1210.         this.loadTabExecute();
  1211.         break;
  1212.       case "tab-dbinfo":
  1213.         this.loadTabDbInfo();
  1214.         break;
  1215.       case "tab-exim":
  1216.         $$(sId).collapsed = false;
  1217.         $$("sm-tabs").selectedItem = $$(sId);
  1218.         break;
  1219.       case "tab-udf":
  1220.         $$(sId).collapsed = false;
  1221.         $$("sm-tabs").selectedItem = $$(sId);
  1222.         break;
  1223.     }
  1224.     //closebutton should be shown if exim or udf tab is displayed
  1225.     if (this.getSelectedTabId() != "tab-exim" && this.getSelectedTabId() != "tab-udf" ) {
  1226.       $$("sm-tabs").setAttribute("closebutton", false);
  1227.     }
  1228.     else {
  1229.       $$("sm-tabs").setAttribute("closebutton", true);
  1230.     }
  1231.     return true;
  1232.   },
  1233.  
  1234.   closeTab: function() {
  1235.     var sId = $$("sm-tabs").selectedItem.id;
  1236.     switch(sId) {
  1237.       case "tab-structure":
  1238.       case "tab-browse":
  1239.       case "tab-execute":
  1240.       case "tab-dbinfo":
  1241.         return true;
  1242.         break;
  1243.       case "tab-exim":
  1244.       case "tab-udf":
  1245.         $$(sId).collapsed = true;
  1246.         break;
  1247.     }
  1248.     var iCurr = $$("sm-tabs").selectedIndex;
  1249.     var iLength = $$("sm-tabs").itemCount;
  1250.     if (iCurr > -1 && iCurr < iLength) {
  1251.       var iNew = iCurr - 1;
  1252.       if (iNew < 0)
  1253.         iNew = iLength - 1;
  1254.       while ($$("sm-tabs").getItemAtIndex(iNew).collapsed) {
  1255.         iNew--;
  1256.         if (iNew < 0)
  1257.           iNew = iLength - 1;
  1258.         if (iNew == iCurr)
  1259.           break;
  1260.       }
  1261.       $$("sm-tabs").selectedIndex = iNew;
  1262.     }
  1263. //    $$("sm-tabs").advanceSelectedTab(-1, true);
  1264. //    while ($$($$("sm-tabs").selectedItem.id).collapsed)
  1265. //      $$("sm-tabs").advanceSelectedTab(-1, true);
  1266.     return true;
  1267.   },
  1268.  
  1269.   //bImplicit: false = called from menuitem; true = function call
  1270.   useExtensionManagementTable: function(bUse, bImplicit) {
  1271.     var mi = $$("menu-general-extensionTable");
  1272.  
  1273.     if(this.sCurrentDatabase == null) {
  1274.       //revert to the state before clicking
  1275.       mi.removeAttribute("checked");
  1276.       if (!bImplicit) alert(sm_getLStr("firstOpenADb"));
  1277.       return false;
  1278.     }
  1279.  
  1280.     smExtManager.setUsage(bUse, bImplicit);
  1281.     if (bUse) {
  1282.       mi.setAttribute("checked", "true");
  1283.       this.populateQueryListbox();
  1284.     }
  1285.     else
  1286.       mi.removeAttribute("checked");
  1287.  
  1288.     //refresh the structure tree here so that mgmt table is shown/removed
  1289.     this.refresh();
  1290.  
  1291.     //hide/show the images for query history in the execute sql tab
  1292.     var aId = ["queryHistoryPrevImage", "queryHistoryNextImage", "querySaveByNameImage", "queryHistoryClearImage", "listbox-queries"];
  1293.     if (bUse)
  1294.       smShow(aId);
  1295.     else
  1296.       smHide(aId);
  1297.  
  1298.     return true;
  1299.   },
  1300.  
  1301.   showPrevSql: function() {
  1302.     var sQuery = smExtManager.getPrevSql();
  1303.     if (!sQuery) return;
  1304.     $$("txtSqlStatement").value = sQuery;
  1305.   },
  1306.  
  1307.   showNextSql: function() {
  1308.     var sQuery = smExtManager.getNextSql();
  1309.     if (!sQuery) return;
  1310.     $$("txtSqlStatement").value = sQuery;
  1311.   },
  1312.  
  1313.   saveSqlByName: function()  {
  1314.     var sQuery = $$("txtSqlStatement").value;
  1315.     if (sQuery.length <= 0)
  1316.       alert(sm_getLStr("sqlm.nothingToSave"));
  1317.  
  1318.     if (smExtManager.saveSqlByName(sQuery))
  1319.       this.populateQueryListbox();
  1320.   },
  1321.  
  1322.   clearSqlHistory: function() {
  1323.     smExtManager.clearSqlHistory();
  1324.   },
  1325.  
  1326.   onSelectQuery: function() {
  1327.     var sVal = $$("listbox-queries").value;
  1328.     if (sVal != this.msQuerySelectInstruction)
  1329.       $$("txtSqlStatement").value = sVal;
  1330.   },
  1331.  
  1332.   populateQueryListbox: function() {
  1333.     var listbox = $$("listbox-queries");
  1334.     if (this.sCurrentDatabase == null) {
  1335.       listbox.hidden = true;
  1336.       return false;
  1337.     }
  1338.     var aQueries = smExtManager.getQueryList();
  1339.     if (aQueries.length)
  1340.       aQueries.unshift(this.msQuerySelectInstruction);
  1341.     else
  1342.       aQueries = [this.msQuerySelectInstruction];
  1343.     var sDefault = listbox.selectedItem;
  1344.     if (sDefault != null)
  1345.       sDefault = sDefault.label;
  1346.     PopulateDropDownItems(aQueries, listbox, sDefault);
  1347.   },
  1348.  
  1349.   runSqlStatement: function(sType) {
  1350.     if(this.getSelectedTabId() != "tab-execute")
  1351.       return false;
  1352.  
  1353.     if(this.sCurrentDatabase == null)  {
  1354.       alert(sm_getLStr("firstOpenADb"));
  1355.       return false;
  1356.     }
  1357.  
  1358.     //get the query string from an xul page
  1359.     var sQuery = $$("txtSqlStatement").value;
  1360.     
  1361.     var queries = sql_tokenizer(sQuery);
  1362.     if (queries.length == 0) {
  1363.       alert(sm_getLStr("writeSomeSql"));
  1364.       return false;
  1365.     }
  1366.     var aData, aColumns, aTypes;
  1367.     var timeElapsed = 0;
  1368.     var bRet = false;
  1369. //    if(sType == "select")
  1370.     if (queries.length == 1) {
  1371.       sQuery = queries[0];
  1372.       bRet = Database.selectQuery(sQuery);
  1373.       timeElapsed = Database.getElapsedTime();
  1374.       //store the query in config db
  1375.       if (bRet) {
  1376.         aData = Database.getRecords();
  1377.         aColumns = Database.getColumns();
  1378.         aTypes = Database.getRecordTypes();
  1379.          sm_message(sm_getLFStr("rowsReturned", [aData.length]), 0x2);
  1380.         smExtManager.addQuery(sQuery);
  1381.       }
  1382.       //set this value so that query history is reset to latest query
  1383.       //that is previous will again begin from the latest query
  1384.       smExtManager.goToLastQuery();
  1385.     }
  1386.     else {
  1387.       bRet = Database.executeTransaction(queries);
  1388.       timeElapsed = Database.getElapsedTime();
  1389.     }
  1390.     
  1391.     //display the last error in the textbox
  1392.     $$("sqlLastError").value = Database.getLastError();
  1393.     if (bRet) {
  1394.       $$("sbQueryTime").label = "ET: " + timeElapsed;
  1395.     }
  1396.  
  1397.     //the following two lines must be before the code for tree
  1398.     //otherwise, it does not refresh the structure tree as expected
  1399.     this.refreshDbStructure();
  1400.     this.loadTabBrowse();
  1401.     
  1402.     treeExecute.ShowTable(false);
  1403.     if (bRet && queries.length == 1) {
  1404.       treeExecute.createColumns(aColumns, 0, [], null);
  1405.       treeExecute.PopulateTableData(aData, aColumns, aTypes);
  1406.     }
  1407.   },
  1408.  
  1409.   newDatabase: function() {
  1410.     var sExt = "." + this.maFileExt[0];
  1411.     //prompt for a file name
  1412.     var fname = prompt(sm_getLFStr("sqlm.enterDatabaseName", [sExt]), "", sm_getLStr("sqlm.enterDatabaseName.title"));
  1413.  
  1414.     //if cancelled, abort
  1415.     if (fname == "" || fname == null)
  1416.       return false;
  1417.     
  1418.     //append the extension to the chosen name
  1419.     fname += sExt;
  1420.     
  1421.     //let the user choose the folder for the new db file  
  1422.     var dir = SmGlobals.chooseDirectory(sm_getLStr("selectFolderForDb"));
  1423.     if (dir != null) {
  1424.       //access this new copied file
  1425.       var newfile = Cc["@mozilla.org/file/local;1"]
  1426.                 .createInstance(Ci.nsILocalFile);
  1427.       newfile.initWithPath(dir.path);
  1428.       newfile.append(fname);
  1429.       
  1430.       //if the file already exists, alert user that existing file will be opened 
  1431.       if(newfile.exists()) {
  1432.         alert(sm_getLStr("dbFileExists"));
  1433.       }
  1434.  
  1435.       //if another file is already open,
  1436.       //confirm from user that it should be closed
  1437.       if(this.closeDatabase(false)) {
  1438.         //assign the new file (nsIFile) to the current database
  1439.         this.sCurrentDatabase = newfile;
  1440.         //if the file does not exist, openDatabase will create it 
  1441.         this.setDatabase(this.sCurrentDatabase);
  1442.         return true;
  1443.       }
  1444.     }
  1445.     return false;
  1446.   },
  1447.  
  1448.   //closeDatabase: 
  1449.   closeDatabase: function(bAlert) {
  1450.     //nothing to close if no database is already open    
  1451.     if(this.sCurrentDatabase == null)  {
  1452.        if(bAlert)
  1453.         alert(sm_getLStr("noOpenDb"));
  1454.       return true;
  1455.     }
  1456.       
  1457.      //if another file is already open, confirm before closing
  1458.      var answer = true;
  1459.      if(bAlert)
  1460.       answer = smPrompt.confirm(null, sm_getLStr("extName"), sm_getLStr("confirmClose"));
  1461.  
  1462.     if(!answer)
  1463.       return false;
  1464.  
  1465.     //if extmgmt table is in use
  1466.     if (smExtManager.getUsage()) {
  1467.       //save StructureTreeState
  1468.       smExtManager.setStructTreeState(smStructTrees[0].aExpandedNodes);
  1469.       //save info on attached tables
  1470.       var aAttached = Database.getAttachedDbList();
  1471.       smExtManager.setAttachedDbList(aAttached);
  1472.     }
  1473.     //make the current database as null and 
  1474.     //call setDatabase to do appropriate things
  1475.     this.sCurrentDatabase = null;
  1476.     this.setDatabase(null);
  1477.     return true;
  1478.   },
  1479.     
  1480.   copyDatabase: function() {
  1481.      if(this.sCurrentDatabase == null) {
  1482.       alert(sm_getLStr("firstOpenADb"));
  1483.       return;
  1484.     }
  1485.     var sExt = "." + this.maFileExt[0];
  1486.     //prompt for a file name
  1487.     var fname = prompt(sm_getLFStr("sqlm.enterDatabaseName", [sExt]), "", sm_getLStr("sqlm.enterDatabaseName.title"));
  1488.  
  1489.     //if cancelled, abort
  1490.     if (fname == "" || fname == null)
  1491.       return;
  1492.     else
  1493.       fname += sExt;
  1494.       
  1495.     //let the user choose the folder for the new db file  
  1496.     //let the user choose the folder for the new db file  
  1497.     var dir = SmGlobals.chooseDirectory(sm_getLStr("selectFolderForDb"));
  1498.     if (dir != null) {
  1499.       //copy the opened file to chosen location
  1500.       this.sCurrentDatabase.copyTo(dir, fname);
  1501.  
  1502.       //access this new copied file
  1503.       var newfile = Cc["@mozilla.org/file/local;1"]
  1504.                 .createInstance(Ci.nsILocalFile);
  1505.       newfile.initWithPath(dir.path);
  1506.       newfile.append(fname);
  1507.       
  1508.       //if the file does not exist, openDatabase will create it 
  1509.       if(!newfile.exists()) {
  1510.         var ans = smPrompt.confirm(null, sm_getLStr("extName"), sm_getLStr("copyFailed"));
  1511.         if(!ans)
  1512.           return;
  1513.       }
  1514.  
  1515.       //assign the new file (nsIFile) to the current database
  1516.       if(this.closeDatabase(false)) {
  1517.         this.sCurrentDatabase = newfile;
  1518.         this.setDatabase(this.sCurrentDatabase);
  1519.         return;
  1520.       }
  1521.     }
  1522.     return;
  1523.   },
  1524.     
  1525.   compactDatabase: function() {
  1526.     if (this.sCurrentDatabase == null) {
  1527.       alert(sm_getLStr("firstOpenADb"));
  1528.       return false;
  1529.     }
  1530.     var befPageCount = Database.getSetting("page_count");
  1531.     var pageSize = Database.getSetting("page_size");
  1532.     var sQuery = "VACUUM";
  1533.     //cannot vacuum from within a transaction
  1534.     Database.selectQuery(sQuery);
  1535.     var aftPageCount = Database.getSetting("page_count");
  1536.     sm_alert(sm_getLStr("vacuum.title"), sm_getLFStr("vacuum.details", [befPageCount, befPageCount*pageSize, aftPageCount, aftPageCount*pageSize]));
  1537.     return true;
  1538.   },
  1539.  
  1540.   analyzeDatabase: function() {
  1541.     if (this.sCurrentDatabase == null) {
  1542.       alert(sm_getLStr("firstOpenADb"));
  1543.       return false;
  1544.     }
  1545.     var sQuery = "ANALYZE";
  1546.     Database.selectQuery(sQuery);
  1547.     return true;
  1548.   },
  1549.  
  1550.   checkIntegrity: function() {
  1551.     if (this.sCurrentDatabase == null) {
  1552.       alert(sm_getLStr("firstOpenADb"));
  1553.       return false;
  1554.     }
  1555.     Database.selectQuery("PRAGMA integrity_check");
  1556.     var records = Database.getRecords();
  1557.     var columns = Database.getColumns();
  1558.  
  1559.     var txt = sm_getLStr("integrityResultPrefix") + ": ";
  1560.     //report OK if i row returned containing the value "ok"
  1561.     if (records.length == 1 && records[0][0] == "ok")
  1562.       alert(txt + sm_getLStr("ok"));
  1563.     else
  1564.       alert(txt + sm_getLStr("notOk"));
  1565.     return true;
  1566.   },
  1567.  
  1568.   openDatabase: function() {        
  1569.     const nsIFilePicker = Ci.nsIFilePicker;
  1570.     var fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
  1571.     fp.init(window, sm_getLStr("selectDb"), nsIFilePicker.modeOpen);
  1572.     var sExt = "";
  1573.     for (var iCnt = 0; iCnt < this.maFileExt.length; iCnt++) {
  1574.       sExt += "*." + this.maFileExt[iCnt] + ";";
  1575.     }
  1576.     fp.appendFilter(sm_getLStr("sqliteDbFiles") + " (" + sExt + ")", sExt);
  1577.     fp.appendFilters(nsIFilePicker.filterAll);
  1578.     
  1579.     var rv = fp.show();
  1580.     if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
  1581.       // work with returned nsILocalFile...
  1582.       if(this.closeDatabase(false)) {
  1583.         this.sCurrentDatabase = fp.file;
  1584.         this.setDatabase(this.sCurrentDatabase);
  1585.         return true;
  1586.       }
  1587.     }
  1588.     return false;
  1589.   },
  1590.  
  1591.   openDatabaseADS: function() {        
  1592.     const nsIFilePicker = Ci.nsIFilePicker;
  1593.     var fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
  1594.     fp.init(window, sm_getLStr("selectDb"), nsIFilePicker.modeOpen);
  1595.     var sExt = "";
  1596.     for (var iCnt = 0; iCnt < this.maFileExt.length; iCnt++) {
  1597.       sExt += "*." + this.maFileExt[iCnt] + ";";
  1598.     }
  1599.     fp.appendFilter(sm_getLStr("sqliteDbFiles") + " (" + sExt + ")", sExt);
  1600.     fp.appendFilters(nsIFilePicker.filterAll);
  1601.     
  1602.     var rv = fp.show();
  1603.     if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
  1604.       var check = {value: false};// default the checkbox to false
  1605.       var input = {value: ""}; // default the edit field to table name
  1606.       var result = smPrompt.prompt(null, sm_getLStr("sqlm.enterADSName") + fp.file.leafName, sm_getLStr("sqlm.enterADSName.descr"), input, null, check);
  1607.       var sAdsName = input.value;
  1608.       //returns true on OK, false on cancel
  1609.       if (!result || sAdsName.length == 0)
  1610.         return false;
  1611.  
  1612.       var sPath = fp.file.path + ":" + sAdsName;
  1613.       return this.openDatabaseWithPath(sPath);
  1614.     }
  1615.     return false;
  1616.   },
  1617.  
  1618.   openDatabaseWithPath: function(sPath) {
  1619.     var newfile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
  1620.     try {
  1621.       newfile.initWithPath(sPath);
  1622.     } catch (e) {
  1623.       alert(sm_getLStr("sqlm.alert.fileNotFound") + sPath);
  1624.       SmGlobals.mru.remove(sPath);
  1625.       return false;
  1626.     }
  1627.     if(newfile.exists()) {
  1628.       if(this.closeDatabase(false)) {
  1629.         this.sCurrentDatabase = newfile;
  1630.         this.setDatabase(this.sCurrentDatabase);
  1631.         return true;
  1632.       }
  1633.     }
  1634.     else {
  1635.       alert(sm_getLStr("sqlm.alert.fileNotFound") + sPath);
  1636.       SmGlobals.mru.remove(sPath);
  1637.     }
  1638.     return false;
  1639.   },
  1640.  
  1641.   saveDatabase: function() {        
  1642.   },
  1643.  
  1644.   openUdfTab: function() {
  1645.     this.loadTabWithId("tab-udf");
  1646.     SmUdf.loadTab();
  1647.   },
  1648.  
  1649.   createTable: function() {        
  1650.     if (this.sCurrentDatabase == null) {
  1651.       alert(sm_getLStr("firstOpenADb"));
  1652.       return false;
  1653.     }
  1654.  
  1655.     var aRetVals = {};
  1656.     window.openDialog("chrome://sqlitemanager/content/createTable.xul", "createTable", "chrome, resizable, centerscreen, modal, dialog", Database, aRetVals);
  1657.      if (aRetVals.ok) {
  1658.       Database.confirmAndExecute([aRetVals.createQuery], sm_getLFStr("sqlm.confirm.createTable", [aRetVals.tableName]), "confirm.create");
  1659.       this.refreshDbStructure();
  1660.        this.loadTabBrowse();
  1661.     }
  1662.   },
  1663.  
  1664.   createObject: function(sObjectType) {
  1665.     if (this.sCurrentDatabase == null) {
  1666.       alert(sm_getLStr("firstOpenADb"));
  1667.       return false;
  1668.     }
  1669.  
  1670.     var xul = "chrome://sqlitemanager/content/create" + sObjectType + ".xul";
  1671.     if (sObjectType == "view") {
  1672.        var aRetVals = {dbName: Database.logicalDbName, tableName: this.aCurrObjNames["table"]};
  1673.       window.openDialog(xul, "create" + sObjectType, 
  1674.               "chrome, resizable, centerscreen, modal, dialog", 
  1675.               Database, aRetVals);
  1676.       if (aRetVals.ok) {
  1677.         Database.confirmAndExecute(aRetVals.queries, sm_getLFStr("sqlm.confirm.createObj", [sObjectType, aRetVals.objectName]), "confirm.create");
  1678.         this.refreshDbStructure();
  1679.          this.loadTabBrowse();
  1680.       }
  1681.     }
  1682.     else
  1683.       window.openDialog(xul, "create" + sObjectType, 
  1684.               "chrome, resizable, centerscreen, modal, dialog", 
  1685.               Database, this.aCurrObjNames["table"], sObjectType);
  1686.  
  1687.     this.refreshDbStructure();
  1688.     this.loadTabBrowse();
  1689.     return true;
  1690.   },
  1691.  
  1692.   modifyView: function() {
  1693.     var sViewName = this.aCurrObjNames["view"];
  1694.     var info = Database.getMasterInfo(sViewName, "");
  1695.     var sOldSql = info.sql;
  1696.     var sSelect = getViewSchemaSelectStmt(sOldSql);
  1697.  
  1698.     var aRetVals = {dbName: Database.logicalDbName, objectName: sViewName, modify: 1, selectStmt: sSelect};
  1699.     aRetVals.readonlyFlags = ["dbnames", "viewname"];
  1700.     window.openDialog("chrome://sqlitemanager/content/createview.xul", "createView", "chrome, resizable, centerscreen, modal, dialog", Database, aRetVals);
  1701.     if (aRetVals.ok) {
  1702.       Database.confirmAndExecute(aRetVals.queries, sm_getLFStr("sqlm.confirm.modifyView", [aRetVals.objectName]), "confirm.create");
  1703.       this.refreshDbStructure();
  1704.        this.loadTabBrowse();
  1705.     }
  1706.   },
  1707.  
  1708.   modifyTable: function(sTableName) {
  1709. //    alert("modtab: " + sTableName);
  1710.   },
  1711.  
  1712.   cancelEditColumn: function() {
  1713.     $$("gb-editColumn").hidden = true;
  1714.   },
  1715.  
  1716.   startEditColumn: function() {
  1717. //    var bConfirm = sm_confirm(sm_getLStr("dangerous.op"), "This is a potentially dangerous operation. SQLite does not support statements that can alter a column in a table. Here, we attempt to reconstruct the new CREATE SQL statement by looking at the pragma table_info which does not contain complete information about the structure of the existing table.\n\n" + sm_getLStr("q.proceed"));
  1718. //    if (!bConfirm)
  1719. //      return false;
  1720.  
  1721.     var treeCol = $$("treeTabCols");
  1722.     var row = treeCol.view.selection.currentIndex;
  1723.     var col = treeCol.columns.getColumnAt(1);
  1724.     var sOldName = treeCol.view.getCellText(row, col);
  1725.     var col = treeCol.columns.getColumnAt(2);
  1726.     var sOldType = treeCol.view.getCellText(row, col);
  1727.     var col = treeCol.columns.getColumnAt(4);
  1728.     var sOldDefault = treeCol.view.getCellText(row, col);
  1729.  
  1730.     var sTable = treeCol.getAttribute("smTableName");
  1731.     $$("tb-ec-table").value = sTable;
  1732.  
  1733.     $$("tb-ec-oldName").value = sOldName;
  1734.     $$("tb-ec-oldType").value = sOldType;
  1735.     $$("tb-ec-oldDefault").value = sOldDefault;
  1736.     $$("tb-ec-newName").value = sOldName;
  1737.     $$("tb-ec-newType").value = sOldType;
  1738.     $$("tb-ec-newDefault").value = sOldDefault;
  1739.  
  1740.     $$("gb-editColumn").hidden = false;
  1741.     $$("tb-ec-newName").focus();
  1742.   },
  1743.  
  1744.   alterColumn: function() {
  1745.     var bConfirm = sm_confirm(sm_getLStr("dangerous.op"), sm_getLStr("sqlm.confirm.dangerousOp") + sm_getLStr("q.proceed"));
  1746.     if (!bConfirm)
  1747.       return false;
  1748.  
  1749.     var sTable = $$("tb-ec-table").value;
  1750.     var sOldName = $$("tb-ec-oldName").value;
  1751.     var sNewName = $$("tb-ec-newName").value;
  1752.     if (sNewName.length == 0) {
  1753.       alert(sm_getLStr("sqlm.alterColumn.name"));
  1754.       return false;
  1755.     }
  1756.     var sNewType = $$("tb-ec-newType").value;
  1757.     var sNewDefVal = $$("tb-ec-newDefault").value;
  1758.     if (sNewDefVal.length == 0)
  1759.       sNewDefVal = null;
  1760.  
  1761.     var aNewInfo = {oldColName: sOldName,
  1762.                     newColName: sNewName,
  1763.                     newColType: sNewType,
  1764.                     newDefaultValue: sNewDefVal};
  1765.     var bReturn = CreateManager.modifyTable("alterColumn", sTable, aNewInfo);
  1766.     if(bReturn) {
  1767.       this.cancelEditColumn();
  1768.  
  1769.       this.refreshDbStructure();
  1770.       this.loadTabBrowse();
  1771.     }
  1772.     return bReturn;
  1773.   },
  1774.  
  1775.   dropColumn: function() {        
  1776.     var bConfirm = sm_confirm(sm_getLStr("dangerous.op"), sm_getLStr("sqlm.confirm.dangerousOp") + sm_getLStr("q.proceed"));
  1777.     if (!bConfirm)
  1778.       return false;
  1779. //    var bConfirm = sm_prefsBranch.getBoolPref("allowUnsafeTableAlteration");
  1780.     var treeCol = $$("treeTabCols");
  1781.     var row = treeCol.view.selection.currentIndex;
  1782.     var col = treeCol.columns.getColumnAt(1);
  1783.     var sColumn = treeCol.view.getCellText(row, col);
  1784.     var sTable = treeCol.getAttribute("smTableName");
  1785.  
  1786.     var bReturn = CreateManager.modifyTable("dropColumn", sTable, sColumn);
  1787.     if(bReturn) {
  1788.       this.refreshDbStructure();
  1789.       this.loadTabBrowse();
  1790.     }
  1791.     return bReturn;
  1792.   },
  1793.  
  1794.   reindexIndex: function() {        
  1795.     var sCurrIndex = this.aCurrObjNames["index"];
  1796.     if(sCurrIndex != null && sCurrIndex != undefined && sCurrIndex.length > 0) {
  1797.       var bReturn = Database.reindexObject("INDEX", sCurrIndex);
  1798.       return bReturn;
  1799.     }
  1800.     return false;
  1801.   },
  1802.  
  1803.   dropObject: function(sObjectType) {
  1804.     if (this.sCurrentDatabase == null) {
  1805.       alert(sm_getLStr("firstOpenADb"));
  1806.       return false;
  1807.     }
  1808.  
  1809.     var sObjectName = "";
  1810.     sObjectName = this.aCurrObjNames[sObjectType];
  1811.     
  1812.     var aNames = this.aObjNames[sObjectType];
  1813.  
  1814.     if(aNames.length == 0) {
  1815.       alert(sm_getLStr("noObjectToDelete") + ": " + sObjectType);
  1816.       return false;
  1817.     }
  1818.     var bReturn = Database.dropObject(sObjectType, sObjectName);
  1819.     if(bReturn) {
  1820.       sm_message(sm_getLStr("dropDone"), 0x2);
  1821.       this.refreshDbStructure();
  1822.       this.loadTabBrowse();
  1823.     }
  1824.     return bReturn;
  1825.   },
  1826.  
  1827.   exportAll: function(sWhat) {
  1828.     if (this.sCurrentDatabase == null) {
  1829.       alert(sm_getLStr("firstOpenADb"));
  1830.       return false;
  1831.     }
  1832.     var sDbName = Database.logicalDbName; //"main";
  1833.     var sExpType = "sql";
  1834.     var sFileName = sDbName;
  1835.     if (sDbName == "main") {
  1836.       sFileName = Database.getFileName();
  1837.       var iPos = sFileName.lastIndexOf(".");
  1838.       if (iPos > 0)
  1839.         sFileName = sFileName.substr(0, iPos);
  1840.     }
  1841.     // get export file
  1842.     const nsIFilePicker = Ci.nsIFilePicker;
  1843.     var fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
  1844.     fp.init(window, sm_getLStr("sqlm.export.fp.title"), nsIFilePicker.modeSave);
  1845.     fp.appendFilters(nsIFilePicker.filterAll);
  1846.     fp.defaultString = sFileName + "." + sExpType;
  1847.     
  1848.     var rv = fp.show();
  1849.     
  1850.     //if chosen then
  1851.     if (rv != nsIFilePicker.returnOK && rv != nsIFilePicker.returnReplace) {
  1852.       alert(sm_getLStr("sqlm.export.fp.descr"));
  1853.       return false;
  1854.     }
  1855.     var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
  1856.     file.initWithFile(fp.file);
  1857.  
  1858.     var foStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
  1859.     // use 0x02 | 0x10 to open file for appending.
  1860.     foStream.init(file, 0x02 | 0x08 | 0x20, 0664, 0); // write, create, truncate
  1861.  
  1862.     var os = Cc["@mozilla.org/intl/converter-output-stream;1"].createInstance(Ci.nsIConverterOutputStream);
  1863.     
  1864.     // This assumes that fos is the nsIOutputStream you want to write to
  1865.     os.init(foStream, "UTF-8", 0, 0x0000);
  1866.     
  1867.     if (sWhat == "tables" || sWhat == "db") {
  1868.       var bCreate = true, bTransact = false;
  1869.       var iExportNum = 0;
  1870.        var aTableNames = Database.getObjectList("table", sDbName);
  1871.       for (var i = 0; i < aTableNames.length; i++) {
  1872.         iExportNum = SmExim.writeSqlContent(os, sDbName, aTableNames[i], bCreate, bTransact);
  1873.       }
  1874.     }
  1875.     var aObjNames = [];
  1876.     if (sWhat == "dbstructure") {
  1877.        var aTableNames = Database.getObjectList("table", sDbName);
  1878.       aObjNames = aObjNames.concat(aTableNames);
  1879.     }
  1880.     if (sWhat == "db" || sWhat == "dbstructure") {
  1881.        var aViewNames = Database.getObjectList("view", sDbName);
  1882.       aObjNames = aObjNames.concat(aViewNames);
  1883.        var aTriggerNames = Database.getObjectList("trigger", sDbName);
  1884.       aObjNames = aObjNames.concat(aTriggerNames);
  1885.        var aIndexNames = Database.getObjectList("index", sDbName);
  1886.       aObjNames = aObjNames.concat(aIndexNames);
  1887.       for (var i = 0; i < aObjNames.length; i++) {
  1888.         var sSql = Database.getMasterInfo(aObjNames[i], sDbName);
  1889.         if (sSql.sql != null)
  1890.           os.writeString(sSql.sql + ";\n");
  1891.       }
  1892.     }
  1893.     os.close();
  1894.     foStream.close();
  1895.  
  1896.     if (sWhat == "db")
  1897.       sm_message(sm_getLFStr("sqlm.export.db", [fp.file.path]), 0x3);
  1898.     if (sWhat == "dbstructure")
  1899.       sm_message(sm_getLFStr("sqlm.export.dbstructure", [fp.file.path]), 0x3);
  1900.     if (sWhat == "tables")
  1901.       sm_message(sm_getLFStr("sqlm.export.tables", [aTableNames.length, fp.file.path]), 0x3);
  1902.     return true;
  1903.   },
  1904.  
  1905.   importFromFile: function() {
  1906.     if (this.sCurrentDatabase == null) {
  1907.       alert(sm_getLStr("firstOpenADb"));
  1908.       return false;
  1909.     }
  1910.     this.loadTabWithId("tab-exim");
  1911.     SmExim.loadDialog("import");
  1912.   },
  1913.   
  1914.   exportObject: function(sObjectType) {
  1915.     if (this.sCurrentDatabase == null) {
  1916.       alert(sm_getLStr("firstOpenADb"));
  1917.       return false;
  1918.     }
  1919.     this.loadTabWithId("tab-exim");
  1920.  
  1921.     var sObjectName = this.aCurrObjNames[sObjectType];
  1922.     SmExim.loadDialog("export", sObjectType, sObjectName);
  1923.     return true;
  1924.   },
  1925.  
  1926.   copyTable: function(sTableName) {
  1927.     var xul = "chrome://sqlitemanager/content/copyTable.xul";
  1928.     var aRetVals = {};
  1929.     var ret = window.openDialog(xul, "copyTable", "chrome, centerscreen, modal, dialog", Database.logicalDbName, this.aCurrObjNames["table"], Database.getDatabaseList(), aRetVals);
  1930.     var sNewDb = aRetVals.newDbName;
  1931.     var sNewTable = aRetVals.newTableName;
  1932.     var bOnlyStructure = aRetVals.onlyStructure;
  1933.  
  1934.     if (sNewTable.length == 0)
  1935.       return false;
  1936.       
  1937.     var info = Database.getMasterInfo(sTableName, "");
  1938.     var r_sql = info.sql;
  1939.     sNewTable = Database.getPrefixedName(sNewTable, sNewDb);
  1940.     var sOldTable = Database.getPrefixedName(sTableName, "");  
  1941.  
  1942.     var sNewSql = replaceObjectNameInSql(r_sql, sNewTable);
  1943.     if (sNewSql == "") {
  1944.       alert(sm_getLStr("sqlm.copyTable.newSqlFailed"));
  1945.       return;
  1946.     }
  1947.  
  1948.     var aQueries = [sNewSql];
  1949.     if(!bOnlyStructure) {
  1950.       aQueries.push("INSERT INTO " + sNewTable + " SELECT * FROM " + sOldTable);
  1951.     }
  1952.     return Database.confirmAndExecute(aQueries, sm_getLStr("sqlm.copyTable.confirm") + ": " + sTableName);
  1953.   },
  1954.  
  1955.   renameTable: function(sTableName)  {
  1956.     var check = {value: false};
  1957.     var input = {value: sTableName};
  1958.     var result = smPrompt.prompt(null, sm_getLFStr("sqlm.renameTable", [sTableName]), sm_getLStr("sqlm.renameTable.descr"), input, null, check);
  1959.     var sNewName = input.value;
  1960.     //returns true on OK, false on cancel
  1961.     if (!result || sNewName.length == 0)
  1962.       return false;
  1963.     return Database.renameTable(sTableName, sNewName, '');
  1964.   },
  1965.  
  1966.   renameObject: function(sObjType)  {
  1967.     var sObjName = this.aCurrObjNames[sObjType];
  1968.     var check = {value: false};   // default the checkbox to false
  1969.     var input = {value: sObjName};   // default the edit field to object name
  1970.     var result = smPrompt.prompt(null, sm_getLFStr("sqlm.renameObj", [sObjType, sObjName]), sm_getLFStr("sqlm.renameObj.descr", [sObjType]), input, null, check);
  1971.     var sNewName = input.value;
  1972.     //returns true on OK, false on cancel
  1973.     if (!result || sNewName.length == 0)
  1974.       return false;
  1975.       
  1976.     sNewName = Database.getPrefixedName(sNewName, "");
  1977.     var info = Database.getMasterInfo(sObjName, "");
  1978.     var sOldSql = info.sql;
  1979.     var sNewSql = replaceObjectNameInSql(sOldSql, sNewName);
  1980.     if (sNewSql == "") {
  1981.       alert(sm_getLStr("sqlm.renameObj.newSqlFailed"));
  1982.       return;
  1983.     }
  1984.     var sOldName = Database.getPrefixedName(sObjName, "");
  1985.  
  1986.     var aQueries = [];
  1987.     aQueries.push("DROP " + sObjType + " " + sOldName);
  1988.     aQueries.push(sNewSql);
  1989.     var bReturn = Database.confirmAndExecute(aQueries, sm_getLFStr("sqlm.renameObj.confirm", [sObjType, sOldName]));
  1990.     if(bReturn)  this.refresh();
  1991.   },
  1992.  
  1993. // operateOnTable: various operations on a given table
  1994. // sOperation = rename | copy | reindex | delete  | 
  1995. //              insert | duplicate | update
  1996.   operateOnTable: function(sOperation) {
  1997.     //these operations make sense in the context of some table
  1998.     //so, take action only if there is a valid selected db and table
  1999.     if (this.sCurrentDatabase == null || this.aCurrObjNames["table"] == null) {
  2000.       alert(sm_getLStr("noDbOrTable"));
  2001.       return false;
  2002.     }
  2003.     var sCurrTable = this.aCurrObjNames["table"];
  2004.     var bReturn = false;
  2005.     var bRefresh = false; //to reload tabs
  2006.     switch(sOperation) {
  2007.       case "reindex":
  2008.         return Database.reindexObject("TABLE", sCurrTable);
  2009.         break;
  2010.       case "analyze":
  2011.         return Database.analyzeTable(sCurrTable);
  2012.         break;
  2013.     }
  2014.     if(sOperation == "copy") {
  2015.       var bReturn = this.copyTable(sCurrTable);
  2016.       if(bReturn)  this.refresh();
  2017.       return bReturn;
  2018.     }
  2019.     if(sOperation == "rename") {
  2020.       var bReturn = this.renameTable(sCurrTable);
  2021.       if(bReturn)  this.refresh();
  2022.       return bReturn;
  2023.     }
  2024.     if(sOperation == "drop") {
  2025.       var bReturn = Database.dropObject("TABLE", sCurrTable);
  2026.       if(bReturn)  this.refresh();
  2027.       return bReturn;
  2028.     }
  2029.     if(sOperation == "modify") {
  2030.       this.modifyTable(sCurrTable);
  2031.       return;
  2032.     }
  2033.     if(sOperation == "empty") {
  2034.       var bReturn = Database.emptyTable(sCurrTable);
  2035.       if(bReturn)  this.refresh();
  2036.       return bReturn;
  2037.     }
  2038.     if(sOperation == "addColumn") {
  2039.       var newCol = [];
  2040.       newCol["name"] = $$("tb-addcol-name").value;
  2041.       newCol["type"] = $$("tb-addcol-type").value;
  2042.       newCol["notnull"] = $$("tb-addcol-notnull").checked;
  2043.       newCol["dflt_value"] = $$("tb-addcol-default").value;
  2044.       newCol["dflt_value"] = SQLiteFn.makeDefaultValue(newCol["dflt_value"]);
  2045.  
  2046.       var bReturn = Database.addColumn(sCurrTable, newCol);
  2047.       if(bReturn) {
  2048.         $$("tb-addcol-name").value = "";
  2049.         $$("tb-addcol-type").value = "";
  2050.         $$("tb-addcol-notnull").checked = false;
  2051.         $$("tb-addcol-default").value = "";
  2052.         this.refresh();
  2053.       }
  2054.       $$("tb-addcol-name").focus();
  2055.       return bReturn;
  2056.     }
  2057.  
  2058.     //update the first selected row in the tree, else alert to select
  2059.     //if selection exists, pass the rowid as the last arg of openDialog
  2060.     var aRowIds = [];
  2061.     var rowCriteria = "";
  2062.     if(sOperation == "update" || sOperation == "delete" || sOperation == "duplicate") {
  2063.       var colMain = Database.getTableRowidCol(this.aCurrObjNames["table"]);
  2064.       colMain["name"] = SQLiteFn.quoteIdentifier(colMain["name"]);
  2065.  
  2066.       //allowing for multiple selection in the tree
  2067.       var tree = $$("browse-tree");
  2068.       var start = new Object();
  2069.       var end = new Object();
  2070.       var numRanges = tree.view.selection.getRangeCount();
  2071.  
  2072.       for (var t = 0; t < numRanges; t++) {
  2073.         tree.view.selection.getRangeAt(t,start,end);
  2074.         for (var v = start.value; v <= end.value; v++) {
  2075.           var rowid = tree.view.getCellText(v,
  2076.               tree.columns.getColumnAt(colMain["cid"]));
  2077.           aRowIds.push(rowid);
  2078.         }
  2079.       }
  2080.       //do nothing, if nothing is selected
  2081.       if(aRowIds.length == 0)  {
  2082.         alert(sm_getLStr("noRecord"));
  2083.         return false;
  2084.       }
  2085.       //if editing, should select only one record
  2086.       if (sOperation == "update" || sOperation == "duplicate")  {
  2087.         if (aRowIds.length != 1) {
  2088.           alert(sm_getLStr("onlyOneRecord"));
  2089.           return false;
  2090.         }
  2091.         rowCriteria = " " + colMain["name"] + " = " + aRowIds[0];
  2092.       }
  2093.       //if deleting, pass as argument rowid of all selected records to delete
  2094.       if (sOperation == "delete") {
  2095.         var criteria = colMain["name"] + " IN (" + aRowIds.toString() + ")";
  2096.         var sQuery = "DELETE FROM " + Database.getPrefixedName(sCurrTable, "") + " WHERE " + criteria;
  2097.         //IMPORTANT: the last parameter is totally undocumented.
  2098.         var bReturn = Database.confirmAndExecute([sQuery], [sm_getLFStr("sqlm.deleteRecs", [aRowIds.length, sCurrTable]), false]);
  2099.         if(bReturn)
  2100.           this.loadTabBrowse();
  2101.         return bReturn;
  2102.       }
  2103.     }
  2104. /* following code if dialog is popped up for editing etc. */
  2105.     var bUseWindow = true;
  2106.     if (bUseWindow) {
  2107.       window.openDialog("chrome://sqlitemanager/content/RowOperations.xul", "RowOperations", "chrome, resizable, centerscreen, modal, dialog", Database, this.aCurrObjNames["table"], sOperation, rowCriteria);
  2108.       if(sOperation != "update") {
  2109.         this.refreshDbStructure();
  2110.       }
  2111.       this.loadTabBrowse();
  2112.     }
  2113.     else {
  2114.       RowOps.loadDialog(this.aCurrObjNames["table"], sOperation, rowCriteria);
  2115.     }
  2116.  
  2117.     return true;
  2118.   },
  2119.  
  2120.   selectDefaultDir: function(sType) {
  2121.     var file = SmGlobals.chooseDirectory(sm_getLStr("sqlm.selectDefaultDir"));
  2122.  
  2123.     // 1. Write to prefs
  2124.     var relFile = Cc["@mozilla.org/pref-relativefile;1"]
  2125.                   .createInstance(Ci.nsIRelativeFilePref);
  2126.     relFile.relativeToKey = "ProfD";
  2127.     relFile.file = file;      // |file| is nsILocalFile
  2128.     sm_prefsBranch.setComplexValue("userDir", Ci.nsIRelativeFilePref, relFile);
  2129.     this.populateDBList("user");
  2130.   },
  2131.  
  2132.   // populateDBList: Load list of files with default file extensions
  2133.   populateDBList: function(sType) {
  2134.     var fileList;
  2135.     var sTooltip = sm_getLStr("sqlm.tooltip.profileDir");
  2136.     var sSelectString = sm_getLStr("selectProfileDb");
  2137.     if (sType == "profile")
  2138.       // Get the nsIFile object pointing to the profile directory
  2139.       fileList = Cc["@mozilla.org/file/directory_service;1"]
  2140.             .getService(Ci.nsIProperties).get("ProfD", Ci.nsIFile)
  2141.             .directoryEntries;
  2142.     if (sType == "user") {
  2143.       sSelectString = sm_getLStr("selectDbInDefaultDir");
  2144.       //Read from prefs
  2145.       var value = sm_prefsBranch.getComplexValue("userDir",Ci.nsIRelativeFilePref);
  2146.       // |value.file| is the file.
  2147.       sTooltip = value.file.path;
  2148.       var lFile = value.file;
  2149.       fileList = lFile.directoryEntries;
  2150.     }
  2151.     //get the node for the popup menus to show profile db list
  2152.     var listbox = $$("listbox-profileDB");
  2153.     listbox.setAttribute("dirType", sType);
  2154.     listbox.setAttribute("tooltiptext", sTooltip);
  2155.     $$("menu-DbList").setAttribute("tooltiptext", sTooltip);
  2156.  
  2157.     listbox.removeAllItems();
  2158.     listbox.appendItem(sSelectString, "");
  2159.     listbox.selectedIndex = 0;
  2160.  
  2161.     var aSplit, sExt;
  2162.     var file;
  2163.     var iFileCount = 0;
  2164.     while (fileList.hasMoreElements()) {
  2165.       file = fileList.getNext().QueryInterface(Ci.nsIFile);
  2166.       aSplit = file.leafName.split(".");
  2167.       sExt = aSplit[aSplit.length - 1]; 
  2168.  
  2169.       if (this.maFileExt.indexOf(sExt) != -1) {
  2170.         iFileCount++;
  2171.         listbox.appendItem(file.leafName, file.path);
  2172.       }
  2173.     }
  2174.     sm_message(sm_getLStr("filesInProfileDbList") + ": " + iFileCount, 0x2);
  2175.   },
  2176.  
  2177.   // openSelectedDatabase: open a file from the database dropdown list
  2178.   openSelectedDatabase: function(sMenuListId) { 
  2179.     //get the node for dropdown menu in which profile db list is shown       
  2180.     var listbox = $$(sMenuListId);
  2181.     var sPath = listbox.selectedItem.value;
  2182.     var sType = listbox.getAttribute("dirType"); //profile/user
  2183.  
  2184.     var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
  2185.     file.initWithPath(sPath);
  2186.  
  2187.     //proceed only if the file exists
  2188.     //we are in the profile folder via the listbox, so open if the file exists
  2189.     //do not attempt to create new file
  2190.     if(!file.exists()) {
  2191.       alert(sm_getLStr("invalidProfileDb"));
  2192.       return false;
  2193.     }
  2194.     if(this.closeDatabase(false))  {
  2195.       this.sCurrentDatabase = file;
  2196.       this.setDatabase(this.sCurrentDatabase);
  2197.       return true;
  2198.     }
  2199.     return false;
  2200.   },
  2201.  
  2202.   changeAttachedDb: function() {
  2203.     var mlist = $$("ml-dbNames");
  2204.     var mi = mlist.selectedItem;
  2205.     var sDbName = mi.getAttribute("dbName");
  2206.     if (sDbName == "")
  2207.      return false;
  2208.  
  2209.     Database.setLogicalDbName(sDbName);
  2210.     this.refreshDbStructure();
  2211.     return true;
  2212.   },
  2213.  
  2214.   detachDatabase: function() {
  2215.     var mlist = $$("ml-dbNames");
  2216.     var mi = mlist.selectedItem;
  2217.     var sDbName = mi.getAttribute("dbName");
  2218.     if (mlist.selectedIndex <= 2) {
  2219.       alert(sm_getLStr("sqlm.detachDb.alert"));
  2220.       return false;
  2221.     }
  2222.  
  2223.     var answer = smPrompt.confirm(null, sm_getLStr("extName"), sm_getLFStr("sqlm.detachDb.confirm", [sDbName]) + mi.getAttribute("tooltiptext"));
  2224.     if(!answer) {
  2225.       return false;
  2226.      } 
  2227.     var sQuery = "DETACH DATABASE " + SQLiteFn.quoteIdentifier(sDbName);
  2228.     if (Database.selectQuery(sQuery)) {
  2229.       var mi = mlist.removeItemAt(mlist.selectedIndex);
  2230.       mlist.selectedIndex = 0;
  2231.       this.changeAttachedDb();
  2232.       sm_message(sm_getLFStr("sqlm.detachDb.msgOk", [sDbName]), 0x2);
  2233.       return true;
  2234.     }
  2235.     else {
  2236.       sm_message(sm_getLFStr("sqlm.detachDb.msgFailed", [sDbName]), 0x2);
  2237.       return false;
  2238.     }
  2239.   },
  2240.  
  2241.   attachDatabase: function() {
  2242.     if(this.sCurrentDatabase == null)  {
  2243.       alert(sm_getLStr("firstOpenADb"));
  2244.       return;
  2245.     }
  2246.     const nsIFilePicker = Ci.nsIFilePicker;
  2247.     var fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
  2248.     fp.init(window, sm_getLStr("selectDb"), nsIFilePicker.modeOpen);
  2249.     var sExt = "";
  2250.     for (var iCnt = 0; iCnt < this.maFileExt.length; iCnt++) {
  2251.       sExt += "*." + this.maFileExt[iCnt] + ";";
  2252.     }
  2253.     fp.appendFilter(sm_getLStr("sqliteDbFiles") + " (" + sExt + ")", sExt);
  2254.     fp.appendFilters(nsIFilePicker.filterAll);
  2255.     
  2256.     var rv = fp.show();
  2257.     if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
  2258.       // work with returned nsILocalFile...
  2259.       var sPath = fp.file.path;
  2260.  
  2261.       var check = {value: false};
  2262.       var input = {value: ""};
  2263.       var result = smPrompt.prompt(null, sm_getLFStr("sqlm.attachDb", [sPath]), sm_getLStr("sqlm.attachDb.descr"), input, null, check);
  2264.       var sDbName = input.value;
  2265.       //returns true on OK, false on cancel
  2266.       if (!result || sDbName.length == 0)
  2267.         return false;
  2268.  
  2269.       if (Database.attachDatabase(sDbName, sPath)) {
  2270.         var mi = $$("ml-dbNames").appendItem(sDbName, sDbName, fp.file.leafName);
  2271.         mi.setAttribute("dbName", sDbName);
  2272.         mi.setAttribute("tooltiptext", sPath);
  2273.  
  2274.         sm_message(sm_getLFStr("sqlm.attachDb.msgOk", [sPath, sDbName]), 0x2);
  2275.         return true;
  2276.       }
  2277.       else {
  2278.         sm_message(sm_getLFStr("sqlm.attachDb.msgFailed", [sPath]), 0x2);
  2279.         return false;
  2280.       }      
  2281.     }
  2282.     return false;
  2283.   },
  2284.  
  2285.   initDbListMenu: function(leafName, path) {
  2286.     var mlist = $$("ml-dbNames");
  2287.     mlist.removeAllItems();
  2288.  
  2289.     var mi = mlist.appendItem(leafName, leafName, "");
  2290.     mi.setAttribute("dbName", "main");
  2291.     mi.setAttribute("tooltiptext", path);
  2292.  
  2293.     var mi = mlist.appendItem(sm_getLStr("sqlm.tooltip.tempObj"), sm_getLStr("sqlm.tooltip.tempObj"), "");
  2294.     mi.setAttribute("dbName", "temp");
  2295.     mi.setAttribute("tooltiptext", sm_getLStr("sqlm.tooltip.tempDbObj"));
  2296.  
  2297.     var mi = mlist.appendItem(sm_getLStr("sqlm.tooltip.attachedDbs"), sm_getLStr("sqlm.tooltip.attachedDbs"), "");
  2298.     mi.setAttribute("dbName", "");
  2299.     mi.setAttribute("disabled", "true");
  2300.   
  2301.     //attach all db that were attached when this db was last closed
  2302.     var aAttached = smExtManager.getAttachedDbList();
  2303.     for (var i = 0; i < aAttached.length; i++) {
  2304.       var sName = aAttached[i].name;
  2305.       var sPath = aAttached[i].file;
  2306.       if (Database.attachDatabase(sName, sPath)) {
  2307.         var mi = mlist.appendItem(sName, sName, sPath);
  2308.         mi.setAttribute("dbName", sName);
  2309.         mi.setAttribute("tooltiptext", sPath);
  2310.       }
  2311.     }
  2312.     mlist.selectedIndex = 0;
  2313.     this.changeAttachedDb();
  2314.   },
  2315.  
  2316.   createTimestampedBackup: function(nsiFileObj) {
  2317.     if (!nsiFileObj.exists()) //exit if no such file
  2318.       return false;
  2319.  
  2320.     switch (sm_prefsBranch.getCharPref("autoBackup")) {
  2321.       case "off":     return false;
  2322.       case "on":      break;
  2323.       case "prompt":
  2324.         var bAnswer = smPrompt.confirm(null, sm_getLStr("extName"), sm_getLStr("confirmBackup"));
  2325.         if (!bAnswer) return false;
  2326.         break;
  2327.       default:        return false;    
  2328.     }
  2329.  
  2330.     //construct a name for the new file as originalname_timestamp.ext
  2331. //    var dt = new Date();
  2332. //    var sTimestamp = dt.getFullYear() + dt.getMonth() + dt.getDate(); 
  2333.     var sTimestamp = SmGlobals.getISODateTimeFormat(null, "", "s");//Date.now(); 
  2334.     var sFileName = nsiFileObj.leafName;
  2335.     var sMainName = sFileName, sExt = "";
  2336.     var iPos = sFileName.lastIndexOf(".");
  2337.     if (iPos > 0) {
  2338.       sMainName = sFileName.substr(0, iPos);
  2339.       sExt = sFileName.substr(iPos);
  2340.     }
  2341.     var sBackupFileName = sMainName + "_" + sTimestamp + sExt;
  2342.  
  2343.     //copy the file in the same location as the original file
  2344.     try {
  2345.       nsiFileObj.copyTo(null, sBackupFileName);
  2346.     } catch (e) {
  2347.       alert(sm_getLFStr("sqlm.backup.failed", [sBackupFileName, e.message]));
  2348.     }
  2349.     return true;
  2350.   },
  2351.  
  2352.   openMemoryDatabase: function() {
  2353.     if(this.closeDatabase(false)) {
  2354.       this.sCurrentDatabase = "memory";
  2355.       this.setDatabase(this.sCurrentDatabase);
  2356.       return true;
  2357.     }
  2358.     return false;
  2359.   },
  2360.  
  2361.   // setDatabase: set the current database to nsiFileObj
  2362.   // If nsiFileObj is a string, then openSpecialDatabase
  2363.   setDatabase: function(nsiFileObj) {
  2364.   //when passed as arg, works but fails to show .path and .leafName properties
  2365. //      this.sCurrentDatabase = nsiFileObj; 
  2366.  
  2367.     this.mbDbJustOpened = true;
  2368.  
  2369.     var mlist = $$("ml-dbNames");
  2370.     mlist.removeAllItems();
  2371.  
  2372.     treeBrowse.ShowTable(false);
  2373.     treeExecute.ShowTable(false);
  2374.  
  2375.     $$("sbSharedMode").label = "---";
  2376.  
  2377.     //try connecting to database
  2378.     var bConnected = false;
  2379.     try  {
  2380.       if(this.sCurrentDatabase != null) {
  2381.         if (this.sCurrentDatabase == "memory") {
  2382.           bConnected = Database.openSpecialDatabase("memory");
  2383.         }
  2384.         else {
  2385.          //create backup before opening
  2386.           this.createTimestampedBackup(this.sCurrentDatabase);
  2387.   
  2388.           var mi = $$("menu-general-sharedPagerCache");
  2389.           var bSharedPagerCache = mi.hasAttribute("checked");
  2390.           bConnected = Database.openDatabase(this.sCurrentDatabase,bSharedPagerCache);
  2391.         }
  2392.         smShow(["vb-structureTab", "vb-browseTab", "vb-executeTab", "vb-dbInfoTab"]);
  2393.  
  2394.         $$("bc-dbOpen").removeAttribute("disabled");
  2395.       }
  2396.       if(this.sCurrentDatabase == null) {
  2397.         Database.closeConnection();
  2398.         //call it to hide all things there - Issue #90, etc.
  2399.         $$("bc-dbOpen").setAttribute("disabled", true);
  2400.  
  2401.         this.emptyTabStructure();
  2402.         smHide(["vb-structureTab", "vb-browseTab", "vb-executeTab", "vb-dbInfoTab"]);
  2403.         this.useExtensionManagementTable(false, true);
  2404.       }
  2405.     }
  2406.     catch (e)  {
  2407.       var sTemp = this.sCurrentDatabase.path;
  2408.       this.sCurrentDatabase = null;
  2409.       sm_message("Connect to '" + sTemp + "' failed: " + e, 0x3);
  2410.       return;
  2411.     }
  2412.  
  2413.     var path = "", leafName = "";
  2414.     if (bConnected) {
  2415.       this.miDbInfoCallCount = 0;
  2416.  
  2417.       $$("sbSharedMode").label = Database.getOpenStatus();
  2418.  
  2419.       if (nsiFileObj.path) {
  2420.         path = nsiFileObj.path;
  2421.         leafName = nsiFileObj.leafName;
  2422.         //add this path to mru list
  2423.         SmGlobals.mru.add(path);
  2424.       }
  2425.       else {
  2426.         path = "in-memory database";
  2427.         leafName = "in-memory";
  2428.       }
  2429.  
  2430.       //extension related mgmt info
  2431.       smExtManager = new SMExtensionManager();
  2432.       this.useExtensionManagementTable(smExtManager.getUsage(), true);
  2433.  
  2434.       //init the db menulist with main, temp & attached db
  2435.       this.initDbListMenu(leafName, path);
  2436.  
  2437.       //display the sqlite version in the status bar
  2438.       var sV = sm_getLStr("sqlite") + " " + Database.sqliteVersion;
  2439.       $$("sbSqliteVersion").setAttribute("label",sV);
  2440.  
  2441.       this.createFunctions(false);
  2442.     }
  2443.  
  2444.     if (!bConnected) {
  2445.       this.sCurrentDatabase = null;
  2446.     }
  2447.     //change window title to show db file path
  2448.     document.title = sm_getLStr("extName") + " - " + path;
  2449.     //reload the two tabs
  2450.     this.refreshDbStructure();
  2451.  
  2452.     this.mbDbJustOpened = false;
  2453.   },
  2454.  
  2455.   createFunctions: function(bAppendMode) {
  2456.     if(this.sCurrentDatabase == null)  {
  2457.       sm_log('createFunctions: returning because this.sCurrentDatabase is null');
  2458.       return;
  2459.     }
  2460.  
  2461.     //before creating functions here, remove all
  2462.     if (!bAppendMode)
  2463.       Database.removeAllFunctions();
  2464.  
  2465.     //get all functions that need to be created for this db
  2466.     var udf = SmUdf.getFunctions();
  2467.  
  2468.     for (var fn in udf) {      if (SmGlobals.gecko_1914pre) //for gecko >= 1.9.1.4pre
  2469.         Database.createFunction(udf[fn].fName, udf[fn].fLength, udf[fn].onFunctionCall);
  2470.       else   //for older gecko
  2471.         Database.createFunction(udf[fn].fName, udf[fn].fLength, udf[fn]);
  2472.  
  2473.       sm_log("Loaded user-defined function: " + udf[fn].fName + ", args.length = " + udf[fn].fLength);
  2474.     }
  2475.  
  2476.     //get all functions that need to be created for this db
  2477.     var udf = SmUdf.getAggregateFunctions();
  2478.  
  2479.     for (var fn in udf) {      Database.createAggregateFunction(udf[fn].fName, udf[fn].fLength, udf[fn].objFunc);
  2480.  
  2481.       sm_log("Loaded user-defined aggregate function: " + udf[fn].fName + ", args.length = " + udf[fn].fLength);
  2482.     }
  2483.   },
  2484.  
  2485.   selectAllRecords: function() {
  2486.     var t;
  2487.     if(this.getSelectedTabId() == "tab-browse")
  2488.       t = $$("browse-tree");
  2489.     else if(this.getSelectedTabId() == "tab-execute")
  2490.       t = $$("treeSqlOutput");
  2491.     else
  2492.       return;
  2493.  
  2494.     t.view.selection.selectAll();
  2495.     t.focus();
  2496.   },
  2497.  
  2498.   openOptionsWindow: function(aElt) {
  2499.     var instantApply = SmGlobals.allPrefs.getBoolPref("browser.preferences.instantApply");
  2500.     var features = "chrome,titlebar,toolbar,centerscreen" + (instantApply ? ",dialog=no" : ",modal");
  2501.     openDialog(SmGlobals.chromes.preferences, 'preferences', features);
  2502.   },
  2503.  
  2504.   openConsoleWindow: function(aElt) {
  2505.     window.openDialog(SmGlobals.chromes.console, 'console', 'chrome,resizable,titlebar,toolbar,centerscreen,modal');
  2506.   },
  2507.  
  2508.   openAboutConfigWindow: function(aElt) {
  2509.     window.openDialog(SmGlobals.chromes.aboutconfig, 'aboutconfig', 'chrome,resizable,titlebar,toolbar,centerscreen,modal');
  2510.   },
  2511.  
  2512.   openDomIWindow: function(aElt) {
  2513.     // Load the Window DataSource so that browser windows opened subsequent to DOM Inspector show up in the DOM Inspector's window list.
  2514.     var windowDS = Cc["@mozilla.org/rdf/datasource;1?name=window-mediator"].getService(Ci.nsIWindowDataSource);
  2515.     var tmpNameSpace = {};                         
  2516.     var sl = Cc["@mozilla.org/moz/jssubscript-loader;1"].createInstance(Ci.mozIJSSubScriptLoader);
  2517.     sl.loadSubScript("chrome://inspector/content/hooks.js", tmpNameSpace);
  2518.     tmpNameSpace.inspectDOMDocument(document);
  2519.   },
  2520.  
  2521.   saveBrowseTreeColState: function(aElt) {
  2522.     while (aElt.nodeName != "tree") {
  2523.       aElt = aElt.parentNode;
  2524.     }
  2525.     if (aElt.id == "browse-tree") {
  2526.       var aWidth = [];
  2527.       var aId = [];
  2528.       var aCols = aElt.querySelectorAll("treecol");
  2529.       for (var i = 0; i < aCols.length; i++) {
  2530.         aWidth.push(aCols.item(i).width);
  2531.         aId.push(aCols.item(i).id);
  2532.       }
  2533.       var objColInfo = {};
  2534.       objColInfo.arrWidth = aWidth;
  2535.       objColInfo.arrId = aId;
  2536.       objColInfo.sObjType = aElt.getAttribute("smObjType");
  2537.       objColInfo.sObjName = aElt.getAttribute("smObjName");
  2538.       var jsonObjColInfo = JSON.stringify(objColInfo);
  2539.       smExtManager.saveBrowseTreeColState(objColInfo.sObjType, objColInfo.sObjName, jsonObjColInfo);
  2540. //      alert(jsonObjColInfo);
  2541.     }
  2542.   },
  2543.  
  2544.   setSqlText: function(val) {
  2545.     $$("txtSqlStatement").value = val;
  2546.   }
  2547. };
  2548.  
  2549. SmGlobals.stylerDataTree = {
  2550.   mStyleSheet: null,
  2551.  
  2552.   getStyleSheet: function() {
  2553.     if (this.mStyleSheet != null)
  2554.       return;
  2555.  
  2556.     var cssTreeDataTable = "chrome://sqlitemanager/skin/dynaTreeDataTable.css";
  2557.     var ss = document.styleSheets;
  2558.     for (var i = 0; i < ss.length; i++) {
  2559.       if (ss[i].href == cssTreeDataTable) {
  2560.         this.mStyleSheet = ss[i];
  2561.         return;
  2562.       }
  2563.     }
  2564.   },
  2565.  
  2566.   convert: function() {
  2567.     //if styleDataTree preference is still there and it has a value set by the user, then conversion needed
  2568.     try {
  2569.       if (!sm_prefsBranch.prefHasUserValue("styleDataTree")) {
  2570.         sm_prefsBranch.clearUserPref("styleDataTree");
  2571.         return true;
  2572.       }
  2573.     } catch (e) {
  2574.       return false;
  2575.     }
  2576.  
  2577.     var oOldStyle = sm_prefsBranch.getCharPref("styleDataTree");
  2578.     sm_prefsBranch.clearUserPref("styleDataTree");
  2579.  
  2580.     //TODO:should get the default value here
  2581.     var oNewStyle = sm_prefsBranch.getCharPref("jsonDataTreeStyle");
  2582.     var objNew = JSON.parse(oNewStyle);
  2583.  
  2584.     if (oOldStyle == 'none') {
  2585.       objNew.setting = 'none';
  2586.       var newPref = JSON.stringify(objNew);
  2587.       sm_prefsBranch.setCharPref("jsonDataTreeStyle", newPref);
  2588.       return;
  2589.     }
  2590.  
  2591.     objNew.setting = 'user';
  2592.     try {
  2593.       var objOld = JSON.parse(oOldStyle);
  2594.     } catch (e) {
  2595.       sm_log(e.message + '\nFailed to convert old treeStyle preference "styleDataTree" into new preference "jsonDataTreeStyle"');
  2596.       sm_prefsBranch.clearUserPref("jsonDataTreeStyle");
  2597.       return;
  2598.     }
  2599.  
  2600.     for (var j in objOld) {
  2601.       try {
  2602.         objNew[j]['unselected']['background-color'] = objOld[j][0][1];
  2603.       } catch (e) {}
  2604.       try {
  2605.         objNew[j]['selected']['background-color'] = objOld[j][0][2];
  2606.       } catch (e) {}
  2607.       try {
  2608.         objNew[j]['unselected']['color'] = objOld[j][1][1];
  2609.       } catch (e) {}
  2610.       try {
  2611.         objNew[j]['selected']['color'] = objOld[j][1][2];
  2612.       } catch (e) {}
  2613.     }
  2614.  
  2615.     var newPref = JSON.stringify(objNew);
  2616.     sm_prefsBranch.setCharPref("jsonDataTreeStyle", newPref);
  2617.     return true;
  2618.   },
  2619.  
  2620.   addTreeStyle: function() {
  2621.     try {
  2622.       this.convert();
  2623.     } catch (e) {}
  2624.  
  2625.     this.getStyleSheet();
  2626.     this.deleteAllRules();
  2627.  
  2628.     var oStyle = sm_prefsBranch.getCharPref("jsonDataTreeStyle");
  2629.     var obj = JSON.parse(oStyle);
  2630.     if (obj.setting == 'none') {
  2631.       return true;
  2632.     }
  2633.  
  2634.     var aIdx = ["nullvalue", "integervalue", "floatvalue", "textvalue", "blobvalue"];
  2635.     for (var k = 0; k < aIdx.length; k++) {
  2636.       var j = aIdx[k];
  2637.       var ruleSelCell = 'treechildren::-moz-tree-cell(' + j + ' selected) { ';
  2638.       var ruleCell = 'treechildren::-moz-tree-cell(' + j + ') { ';
  2639.       var ruleSelText = 'treechildren::-moz-tree-cell-text(' + j + ' selected) { ';
  2640.       var ruleText = 'treechildren::-moz-tree-cell-text(' + j + ') { ';
  2641.  
  2642.       if (obj[j]['selected']) {
  2643.         if (obj[j]['selected']['background-color'])
  2644.           ruleSelCell += 'background-color: ' + obj[j]['selected']['background-color'] + "; ";
  2645.         if (obj[j]['selected']['background-color'])
  2646.           ruleSelText += 'color: ' + obj[j]['selected']['color'] + "; ";
  2647.       }
  2648.       if (obj[j]['unselected']) {
  2649.         if (obj[j]['unselected']['background-color'])
  2650.           ruleCell += 'background-color: ' + obj[j]['unselected']['background-color'] + "; ";
  2651.         if (obj[j]['unselected']['background-color'])
  2652.           ruleText += 'color: ' + obj[j]['unselected']['color'] + "; ";
  2653.       }
  2654.  
  2655.       ruleSelCell += "}";
  2656.       ruleCell += "}";
  2657.       ruleSelText += "}";
  2658.       ruleText += "}";
  2659.  
  2660.       //rule for selected should be inserted first
  2661.       this.mStyleSheet.insertRule(ruleSelCell, 0);
  2662.       this.mStyleSheet.insertRule(ruleCell, 0);
  2663.       this.mStyleSheet.insertRule(ruleSelText, 0);
  2664.       this.mStyleSheet.insertRule(ruleText, 0);
  2665.     }
  2666.     return true;
  2667.   },
  2668.  
  2669.   deleteAllRules: function() {
  2670.     if (this.mStyleSheet == null)
  2671.       return;
  2672.  
  2673.     while (this.mStyleSheet.cssRules.length > 0) {
  2674.       this.mStyleSheet.deleteRule(0);
  2675.     }    
  2676.   }
  2677. };
  2678.  
  2679. //this object handles MRU using one preference 'jsonMruData'
  2680. SmGlobals.mru = {
  2681.   mbInit: false,
  2682.   mSize: 0,
  2683.   mList: [],
  2684.   mProfilePath: '',
  2685.  
  2686.   initialize: function() {
  2687.     try {
  2688.       this.convert();
  2689.     } catch (e) {}
  2690.  
  2691.     this.getPref();
  2692.  
  2693.     this.mProfilePath = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties).get("ProfD", Ci.nsIFile).path;
  2694.     this.mbInit = true;
  2695.   },
  2696.  
  2697.   convert: function() {
  2698.     //use the two prefs and remove them; so, the following can happen only once.
  2699.     var sPref = sm_prefsBranch.getComplexValue("mruPath.1", Ci.nsISupportsString).data;
  2700.     this.mList = sPref.split(",");
  2701.     this.mSize = sm_prefsBranch.getIntPref("mruSize");
  2702.  
  2703.     sm_prefsBranch.clearUserPref("mruPath.1");
  2704.     sm_prefsBranch.clearUserPref("mruSize");
  2705.  
  2706.     this.setPref();
  2707.     return true;
  2708.   },
  2709.  
  2710.   add: function(sPath) {
  2711.     if (sPath.indexOf(this.mProfilePath) == 0)
  2712.       sPath = "[ProfD]" + sPath.substring(this.mProfilePath.length);
  2713.  
  2714.     var iPos = this.mList.indexOf(sPath);
  2715.     if (iPos >= 0) {
  2716.       //remove at iPos
  2717.       this.mList.splice(iPos, 1);
  2718.     }
  2719.     //add in the beginning
  2720.     this.mList.splice(0, 0, sPath);
  2721.     
  2722.     if (this.mList.length > this.mSize) {
  2723.       //remove the extra entries
  2724.       this.mList.splice(this.mSize, this.mList.length  - this.mSize);
  2725.     }
  2726.  
  2727.     this.setPref();
  2728.   },
  2729.  
  2730.   remove: function(sPath) {
  2731.     if (sPath.indexOf(this.mProfilePath) == 0)
  2732.       sPath = "[ProfD]" + sPath.substring(this.mProfilePath.length);
  2733.  
  2734.     var iPos = this.mList.indexOf(sPath);
  2735.     if (iPos >= 0) {
  2736.       //remove at iPos
  2737.       this.mList.splice(iPos, 1);
  2738.       this.setPref();
  2739.       return true;
  2740.     }
  2741.     return false;
  2742.   },
  2743.  
  2744.   getList: function() {
  2745.     if (!this.mbInit)
  2746.       this.initialize();
  2747.  
  2748.     var aList = [];
  2749.     for (var i = 0; i < this.mList.length; i++) {
  2750.       aList.push(this.getFullPath(this.mList[i]));
  2751.     }
  2752.     return aList;
  2753.   },
  2754.  
  2755.   getLatest: function() {
  2756.     if (!this.mbInit)
  2757.       this.initialize();
  2758.  
  2759.     if (this.mList.length > 0)
  2760.       return this.getFullPath(this.mList[0]);
  2761.     else
  2762.       return null;
  2763.   },
  2764.  
  2765.   getFullPath: function(sVal) {
  2766.     var sRelConst = "[ProfD]";
  2767.     if (sVal.indexOf(sRelConst) == 0)
  2768.       sVal = this.mProfilePath + sVal.substring(sRelConst.length);
  2769.  
  2770.     return sVal;
  2771.   },
  2772.  
  2773.   getPref: function() {
  2774.     try {
  2775.       var sPref = sm_prefsBranch.getComplexValue("jsonMruData", Ci.nsISupportsString).data;
  2776.     } catch (e) {
  2777.       var sPref = sm_prefsBranch.getCharPref("jsonMruData");
  2778.     }
  2779.     var obj = JSON.parse(sPref);
  2780.     this.mList = obj.list;
  2781.     this.mSize = obj.size;
  2782.   },
  2783.  
  2784.   setPref: function() {
  2785.     try {
  2786.       var sPref = sm_prefsBranch.getComplexValue("jsonMruData", Ci.nsISupportsString).data;
  2787.     } catch (e) {
  2788.       var sPref = sm_prefsBranch.getCharPref("jsonMruData");
  2789.     }
  2790.     var obj = JSON.parse(sPref);
  2791.     obj.list = this.mList;
  2792.     obj.size = this.mSize;
  2793.     sPref = JSON.stringify(obj);
  2794.     sm_setUnicodePref("jsonMruData", sPref);
  2795.   }
  2796. };
  2797.